From 3a1f32c817f0b3f28afb9c87215a7502fe48864c Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 12 Apr 2021 17:56:20 +0300 Subject: feat: add "new-topic" command and "new --topic" option This makes it easier to have "topic pages" in a journal, for collecting entries related to a certain topic. The topic page uses ikiwiki's !inline directive to collect pages linking to it. --- src/bin/jt2.rs | 15 +++++++++++---- src/error.rs | 14 +++++++++++--- src/journal.rs | 32 +++++++++++++++++++++++++++++++- src/opt.rs | 12 ++++++++++++ src/template.rs | 17 +++++++++++++++++ 5 files changed, 82 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/bin/jt2.rs b/src/bin/jt2.rs index 25a3a13..677bb35 100644 --- a/src/bin/jt2.rs +++ b/src/bin/jt2.rs @@ -3,7 +3,7 @@ use jt2::error::JournalError; use jt2::journal::Journal; use jt2::opt::{Opt, SubCommand}; -use std::path::Path; +use std::path::{Path, PathBuf}; use structopt::StructOpt; fn main() -> anyhow::Result<()> { @@ -17,7 +17,8 @@ fn main() -> anyhow::Result<()> { description, } => init(&config.dirname, &journalname, &description, &config)?, SubCommand::IsJournal => is_journal(&config)?, - SubCommand::New { title } => new_draft(&title, &config)?, + SubCommand::New { title, topic } => new_draft(&title, &topic, &config)?, + SubCommand::NewTopic { path, title } => new_topic(&path, &title, &config)?, SubCommand::Edit { draft } => edit_draft(&draft, &config)?, SubCommand::Finish { draft, basename } => finish_draft(&draft, &basename, &config)?, } @@ -41,9 +42,15 @@ fn is_journal(config: &Configuration) -> anyhow::Result<()> { Ok(()) } -fn new_draft(title: &str, config: &Configuration) -> anyhow::Result<()> { +fn new_draft(title: &str, topic: &Option, config: &Configuration) -> anyhow::Result<()> { let journal = Journal::new(&config.dirname, &config.entries)?; - journal.new_draft(title, &config.editor)?; + journal.new_draft(title, topic, &config.editor)?; + Ok(()) +} + +fn new_topic(path: &Path, title: &str, config: &Configuration) -> anyhow::Result<()> { + let journal = Journal::new(&config.dirname, &config.entries)?; + journal.new_topic(path, title, &config.editor)?; Ok(()) } diff --git a/src/error.rs b/src/error.rs index a6f239e..89a10c1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -34,14 +34,22 @@ pub enum JournalError { #[error("failed to create journal entry {0}: {1}")] WriteEntry(PathBuf, #[source] std::io::Error), - /// To many drafts. - #[error("there are already {0} drafts in {1}, can't create more")] - TooManyDrafts(usize, PathBuf), + /// Failed to write topic page. + #[error("failed to create topic page {0}: {1}")] + WriteTopic(PathBuf, #[source] std::io::Error), /// User chose a draft that doesn't exist. #[error("No draft {0} in {1}")] NoSuchDraft(String, PathBuf), + /// Too many drafts. + #[error("there are already {0} drafts in {1}, can't create more")] + TooManyDrafts(usize, PathBuf), + + /// User chose a topic that doesn't exist. + #[error("No topic page {0}")] + NoSuchTopic(PathBuf), + /// Failed to read draft. #[error("failed to drafts {0}: {1}")] ReadDraft(PathBuf, #[source] std::io::Error), diff --git a/src/journal.rs b/src/journal.rs index 788a34b..41e6d3d 100644 --- a/src/journal.rs +++ b/src/journal.rs @@ -57,7 +57,12 @@ impl Journal { self.entries.clone() } - pub fn new_draft(&self, title: &str, editor: &str) -> Result<(), JournalError> { + pub fn new_draft( + &self, + title: &str, + topic: &Option, + editor: &str, + ) -> Result<(), JournalError> { let drafts = self.drafts(); if !drafts.exists() { std::fs::create_dir(&drafts) @@ -67,6 +72,13 @@ impl Journal { let mut context = Context::new(); context.insert("title", title); context.insert("date", ¤t_timestamp()); + if let Some(ref topic) = topic { + let pathname = topic_path(self.dirname(), topic); + if !pathname.exists() { + return Err(JournalError::NoSuchTopic(topic.to_path_buf())); + } + context.insert("topic", &topic.display().to_string()); + } let pathname = self.pick_file_id(&drafts)?; let text = self.templates.new_draft(&context)?; @@ -143,6 +155,18 @@ impl Journal { })?; Ok(()) } + + pub fn new_topic(&self, path: &Path, title: &str, editor: &str) -> Result<(), JournalError> { + let mut context = Context::new(); + context.insert("title", title); + + let pathname = topic_path(self.dirname(), path); + let text = self.templates.new_topic(&context)?; + std::fs::write(&pathname, text) + .map_err(|err| JournalError::WriteTopic(pathname.to_path_buf(), err))?; + self.edit(editor, &pathname)?; + Ok(()) + } } fn is_dir(path: &Path) -> bool { @@ -153,6 +177,12 @@ fn is_dir(path: &Path) -> bool { } } +fn topic_path(dirname: &Path, topic: &Path) -> PathBuf { + let mut path = dirname.join(topic); + path.set_extension("mdwn"); + path +} + fn current_timestamp() -> String { let now = Local::now(); let now: DateTime = DateTime::from(now); diff --git a/src/opt.rs b/src/opt.rs index 5455777..4e6f46d 100644 --- a/src/opt.rs +++ b/src/opt.rs @@ -64,6 +64,18 @@ pub enum SubCommand { New { #[structopt(help = "Title of new draft")] title: String, + + #[structopt(long, help = "Add link to a topic page")] + topic: Option, + }, + + /// Create topic page. + NewTopic { + #[structopt(help = "Path to topic page in journal")] + path: PathBuf, + + #[structopt(help = "Title of topic page")] + title: String, }, /// Invoke editor on journal entry draft. diff --git a/src/template.rs b/src/template.rs index 89c1a15..23b9b06 100644 --- a/src/template.rs +++ b/src/template.rs @@ -4,10 +4,22 @@ use tera::{Context, Tera}; const NEW_ENTRY: &str = r#"[[!meta title="{{ title }}"]] [[!meta date="{{ date }}"]] +{% if topic %} +[[!meta link="{{ topic }}"]] +{% endif %} This is the default template. "#; +const NEW_TOPIC: &str = r#"[[!meta title="{{ title }}"]] + +This is the default topic template. + +# Entries + +[[!inline pages="link(.)" archive=yes reverse=yes trail=yes]] +"#; + pub struct Templates { tera: Tera, } @@ -17,6 +29,7 @@ impl Templates { 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); + add_default_template(&mut tera, "new_topic", NEW_TOPIC); Ok(Self { tera }) } @@ -24,6 +37,10 @@ impl Templates { self.render("new_entry", &context) } + pub fn new_topic(&self, context: &Context) -> Result { + self.render("new_topic", &context) + } + fn render(&self, name: &str, context: &Context) -> Result { match self.tera.render(name, &context) { Ok(s) => Ok(s), -- cgit v1.2.1