summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-04-08 08:24:40 +0300
committerLars Wirzenius <liw@liw.fi>2021-04-08 09:37:21 +0300
commitfc917bf829c8a7cbf950754abc7085ffb00dbf17 (patch)
treedebaa476835926b80af97d47d4df6c7a72c2e0c7
parent518e7dc2d5f97387702af0a300cc2842bd0deec6 (diff)
downloadjt2-fc917bf829c8a7cbf950754abc7085ffb00dbf17.tar.gz
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.
-rw-r--r--Cargo.lock278
-rw-r--r--Cargo.toml1
-rw-r--r--jt.md22
-rw-r--r--src/error.rs8
-rw-r--r--src/journal.rs18
-rw-r--r--src/lib.rs1
-rw-r--r--src/template.rs45
7 files changed, 370 insertions, 3 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 035e089..b6114f6 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -48,6 +48,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
+name = "block-buffer"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b"
+dependencies = [
+ "block-padding",
+ "byte-tools",
+ "byteorder",
+ "generic-array",
+]
+
+[[package]]
+name = "block-padding"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5"
+dependencies = [
+ "byte-tools",
+]
+
+[[package]]
+name = "bstr"
+version = "0.2.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a40b47ad93e1a5404e6c18dec46b628214fee441c70f4ab5d6942142cc268a3d"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "byte-tools"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7"
+
+[[package]]
+name = "byteorder"
+version = "1.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
+
+[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -82,6 +124,26 @@ dependencies = [
]
[[package]]
+name = "crossbeam-utils"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7e9d99fa91428effe99c5c6d4634cdeba32b8cf784fc428a2a687f61a952c49"
+dependencies = [
+ "autocfg",
+ "cfg-if",
+ "lazy_static",
+]
+
+[[package]]
+name = "digest"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "directories-next"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -122,6 +184,27 @@ dependencies = [
]
[[package]]
+name = "fake-simd"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
+
+[[package]]
+name = "fnv"
+version = "1.0.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+
+[[package]]
+name = "generic-array"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
+dependencies = [
+ "typenum",
+]
+
+[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -133,6 +216,30 @@ dependencies = [
]
[[package]]
+name = "globset"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c152169ef1e421390738366d2f796655fec62621dabbd0fd476f905934061e4a"
+dependencies = [
+ "aho-corasick",
+ "bstr",
+ "fnv",
+ "log",
+ "regex",
+]
+
+[[package]]
+name = "globwalk"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93e3af942408868f6934a7b85134a3230832b9977cf66125df2f9edcfce4ddcc"
+dependencies = [
+ "bitflags",
+ "ignore",
+ "walkdir",
+]
+
+[[package]]
name = "heck"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -160,6 +267,30 @@ dependencies = [
]
[[package]]
+name = "ignore"
+version = "0.4.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b287fb45c60bb826a0dc68ff08742b9d88a2fea13d6e0c286b3172065aaf878c"
+dependencies = [
+ "crossbeam-utils",
+ "globset",
+ "lazy_static",
+ "log",
+ "memchr",
+ "regex",
+ "same-file",
+ "thread_local",
+ "walkdir",
+ "winapi-util",
+]
+
+[[package]]
+name = "itoa"
+version = "0.4.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736"
+
+[[package]]
name = "jt2"
version = "0.1.0"
dependencies = [
@@ -172,6 +303,7 @@ dependencies = [
"serde",
"serde_yaml",
"structopt",
+ "tera",
"thiserror",
]
@@ -203,6 +335,12 @@ dependencies = [
]
[[package]]
+name = "maplit"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
+
+[[package]]
name = "memchr"
version = "2.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -228,6 +366,61 @@ dependencies = [
]
[[package]]
+name = "once_cell"
+version = "1.7.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
+
+[[package]]
+name = "opaque-debug"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c"
+
+[[package]]
+name = "pest"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
+dependencies = [
+ "ucd-trie",
+]
+
+[[package]]
+name = "pest_derive"
+version = "2.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
+dependencies = [
+ "pest",
+ "pest_generator",
+]
+
+[[package]]
+name = "pest_generator"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
+dependencies = [
+ "pest",
+ "pest_meta",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "pest_meta"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
+dependencies = [
+ "maplit",
+ "pest",
+ "sha-1",
+]
+
+[[package]]
name = "pretty_env_logger"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -322,6 +515,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5f089152e60f62d28b835fbff2cd2e8dc0baf1ac13343bef92ab7eed84548"
[[package]]
+name = "ryu"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
name = "serde"
version = "1.0.125"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -342,6 +550,17 @@ dependencies = [
]
[[package]]
+name = "serde_json"
+version = "1.0.64"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "799e97dc9fdae36a5c8b8f2cae9ce2ee9fdce2058c57a93e6099d919fd982f79"
+dependencies = [
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
name = "serde_yaml"
version = "0.8.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -354,6 +573,18 @@ dependencies = [
]
[[package]]
+name = "sha-1"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f7d94d0bede923b3cea61f3f1ff57ff8cdfd77b400fb8f9998949e0cf04163df"
+dependencies = [
+ "block-buffer",
+ "digest",
+ "fake-simd",
+ "opaque-debug",
+]
+
+[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -395,6 +626,21 @@ dependencies = [
]
[[package]]
+name = "tera"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cb278a72e426f291faf182cb0e0cb7d20241e8e9881046724ac874a83c62346"
+dependencies = [
+ "globwalk",
+ "lazy_static",
+ "pest",
+ "pest_derive",
+ "regex",
+ "serde",
+ "serde_json",
+]
+
+[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -433,6 +679,15 @@ dependencies = [
]
[[package]]
+name = "thread_local"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8018d24e04c95ac8790716a5987d0fec4f8b27249ffa0f7d33f1369bdfb88cbd"
+dependencies = [
+ "once_cell",
+]
+
+[[package]]
name = "time"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -443,6 +698,18 @@ dependencies = [
]
[[package]]
+name = "typenum"
+version = "1.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
+
+[[package]]
+name = "ucd-trie"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
+
+[[package]]
name = "unicode-segmentation"
version = "1.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -473,6 +740,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
+name = "walkdir"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
+dependencies = [
+ "same-file",
+ "winapi",
+ "winapi-util",
+]
+
+[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index a76755a..30fd0d6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,3 +15,4 @@ serde = { version = "1", features = ["derive"] }
serde_yaml = "0.8"
regex = "1"
chrono = "0.4"
+tera = { version = "1", default-features = false }
diff --git a/jt.md b/jt.md
index 9ee4d1d..1b80c5d 100644
--- a/jt.md
+++ b/jt.md
@@ -224,6 +224,28 @@ then there are no drafts in jrnl
+## Override template for new journal entries
+
+Verify that we can have a custom template for new journal entries.
+
+~~~scenario
+given an installed jt
+
+when I run jt2 --dirname jrnl init default "My test journal"
+then command is successful
+
+given file jrnl/.config/templates/new_entry from new_entry_template
+
+when I run jt2 --editor=none --dirname=jrnl new "Abracadabra"
+then command is successful
+and there is one draft in jrnl
+and draft 0 in jrnl contains "custom new entry template"
+~~~
+
+~~~{#new_entry_template .file .numberLines}
+This is a custom new entry template.
+~~~
+
# Colophon
This document is meant to be processed with the [Subplot][] program to
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<Self, JournalError> {
+ 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<String, JournalError> {
+ self.render("new_entry", &context)
+ }
+
+ fn render(&self, name: &str, context: &Context) -> Result<String, JournalError> {
+ 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");
+ }
+ }
+}