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. --- Cargo.lock | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + jt.md | 22 +++++ src/error.rs | 8 ++ src/journal.rs | 18 +++- src/lib.rs | 1 + src/template.rs | 45 +++++++++ 7 files changed, 370 insertions(+), 3 deletions(-) create mode 100644 src/template.rs diff --git a/Cargo.lock b/Cargo.lock index 035e089..b6114f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,6 +47,48 @@ version = "1.2.1" 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" @@ -81,6 +123,26 @@ dependencies = [ "vec_map", ] +[[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" @@ -121,6 +183,27 @@ dependencies = [ "termcolor", ] +[[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" @@ -132,6 +215,30 @@ dependencies = [ "wasi", ] +[[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" @@ -159,6 +266,30 @@ dependencies = [ "quick-error", ] +[[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" @@ -172,6 +303,7 @@ dependencies = [ "serde", "serde_yaml", "structopt", + "tera", "thiserror", ] @@ -202,6 +334,12 @@ dependencies = [ "cfg-if", ] +[[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" @@ -227,6 +365,61 @@ dependencies = [ "autocfg", ] +[[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" @@ -321,6 +514,21 @@ version = "0.6.23" 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" @@ -341,6 +549,17 @@ dependencies = [ "syn", ] +[[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" @@ -353,6 +572,18 @@ dependencies = [ "yaml-rust", ] +[[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" @@ -394,6 +625,21 @@ dependencies = [ "unicode-xid", ] +[[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" @@ -432,6 +678,15 @@ dependencies = [ "syn", ] +[[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" @@ -442,6 +697,18 @@ dependencies = [ "winapi", ] +[[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" @@ -472,6 +739,17 @@ version = "0.9.3" 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" 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 { + 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 From f30f28037928be75405a84addd9a9bd3242ee15e Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 8 Apr 2021 09:33:35 +0300 Subject: feat: add a timestamp to the default template --- jt.md | 1 + src/journal.rs | 9 ++++++++- src/template.rs | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/jt.md b/jt.md index 1b80c5d..d1f3b14 100644 --- a/jt.md +++ b/jt.md @@ -155,6 +155,7 @@ 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 "Abracadabra" +and draft 0 in jrnl contains "!meta date=" given an executable script append.sh when I run jt2 --editor=./append.sh --dirname=jrnl edit 0 diff --git a/src/journal.rs b/src/journal.rs index e5a692e..788a34b 100644 --- a/src/journal.rs +++ b/src/journal.rs @@ -1,6 +1,6 @@ use crate::error::JournalError; use crate::template::Templates; -use chrono::Local; +use chrono::{DateTime, Local}; use std::path::{Path, PathBuf}; use std::process::Command; use tera::Context; @@ -66,6 +66,7 @@ impl Journal { let mut context = Context::new(); context.insert("title", title); + context.insert("date", ¤t_timestamp()); let pathname = self.pick_file_id(&drafts)?; let text = self.templates.new_draft(&context)?; @@ -151,3 +152,9 @@ fn is_dir(path: &Path) -> bool { false } } + +fn current_timestamp() -> String { + let now = Local::now(); + let now: DateTime = DateTime::from(now); + now.to_rfc2822() +} diff --git a/src/template.rs b/src/template.rs index 1ad8a54..89c1a15 100644 --- a/src/template.rs +++ b/src/template.rs @@ -3,6 +3,7 @@ use std::path::Path; use tera::{Context, Tera}; const NEW_ENTRY: &str = r#"[[!meta title="{{ title }}"]] +[[!meta date="{{ date }}"]] This is the default template. "#; -- cgit v1.2.1 From 0b0cc421e79bff9f8692e8c771c5e72ee4414032 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Thu, 8 Apr 2021 09:39:20 +0300 Subject: refactor: sort dependencies in Cargo.toml in alphabetical order --- Cargo.lock | 20 ++++++++++---------- Cargo.toml | 12 ++++++------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b6114f6..e6a7e11 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,9 +20,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cddc5f91628367664cc7c69714ff08deee8a3efc54623011c772544d7b2767" +checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" [[package]] name = "atty" @@ -166,9 +166,9 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" [[package]] name = "env_logger" @@ -315,9 +315,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.91" +version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8916b1f6ca17130ec6568feccee27c156ad12037880833a3b842a823236502e7" +checksum = "9385f66bf6105b241aa65a61cb923ef20efc665cb9f9bb50ac2f0c4b7f378d41" [[package]] name = "linked-hash-map" @@ -456,9 +456,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec" dependencies = [ "unicode-xid", ] @@ -616,9 +616,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.64" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd9d1e9976102a03c542daa2eff1b43f9d72306342f3f8b3ed5fb8908195d6f" +checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 30fd0d6..6bbe649 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,14 +5,14 @@ authors = ["Lars Wirzenius "] edition = "2018" [dependencies] -structopt = "0.3" anyhow = "1" -thiserror = "1" -pretty_env_logger = "0.4" -log = "0.4" +chrono = "0.4" directories-next = "2.0.0" +log = "0.4" +pretty_env_logger = "0.4" +regex = "1" serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" -regex = "1" -chrono = "0.4" +structopt = "0.3" tera = { version = "1", default-features = false } +thiserror = "1" -- cgit v1.2.1