summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLars Wirzenius <liw@liw.fi>2021-04-12 17:56:20 +0300
committerLars Wirzenius <liw@liw.fi>2021-04-12 18:46:30 +0300
commit3a1f32c817f0b3f28afb9c87215a7502fe48864c (patch)
treef0d39eb5e19be1ebf6f3f53ceb83dfd2ae184e93
parent924fc1add2de3edac4ad4b1097a3bbbb73d0ad50 (diff)
downloadjt2-3a1f32c817f0b3f28afb9c87215a7502fe48864c.tar.gz
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.
-rw-r--r--jt.md26
-rw-r--r--src/bin/jt2.rs15
-rw-r--r--src/error.rs14
-rw-r--r--src/journal.rs32
-rw-r--r--src/opt.rs12
-rw-r--r--src/template.rs17
-rw-r--r--subplot/jt.py7
-rw-r--r--subplot/jt.yaml3
8 files changed, 118 insertions, 8 deletions
diff --git a/jt.md b/jt.md
index d1f3b14..958de77 100644
--- a/jt.md
+++ b/jt.md
@@ -247,6 +247,32 @@ and draft 0 in jrnl contains "custom new entry template"
This is a custom new entry template.
~~~
+## Use topic pages
+
+Verify that we can create a new topic page and a new entry referring
+to that topic page.
+
+~~~scenario
+given an installed jt
+
+when I run jt2 --dirname jrnl init default "My test journal"
+then command is successful
+
+when I try to run jt2 --editor=none --dirname=jrnl new --topic foo "Abracadabra"
+then command fails
+then stderr contains "foo"
+
+when I run jt2 --editor=none --dirname=jrnl new-topic foo "Things about Foo"
+then command is successful
+then file jrnl/foo.mdwn contains "Things about Foo"
+
+when I run jt2 --editor=none --dirname=jrnl new --topic foo "Abracadabra"
+then command is successful
+and there is one draft in jrnl
+and draft 0 in jrnl links to "foo"
+~~~
+
+
# Colophon
This document is meant to be processed with the [Subplot][] program to
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<PathBuf>, 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<PathBuf>,
+ 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", &current_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<Local> = 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<PathBuf>,
+ },
+
+ /// 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<String, JournalError> {
+ self.render("new_topic", &context)
+ }
+
fn render(&self, name: &str, context: &Context) -> Result<String, JournalError> {
match self.tera.render(name, &context) {
Ok(s) => Ok(s),
diff --git a/subplot/jt.py b/subplot/jt.py
index 6ce75b0..d6023b3 100644
--- a/subplot/jt.py
+++ b/subplot/jt.py
@@ -135,6 +135,13 @@ def draft_contains_string(ctx, dirname=None, draftno=None, pattern=None):
assert pattern in data
+def draft_links_to_topic(ctx, dirname=None, draftno=None, topic=None):
+ logging.debug(f"checking draft {draftno} in {dirname} links to {topic!r}")
+ draft_contains_string(
+ ctx, dirname=dirname, draftno=draftno, pattern=f'\n[[!meta link="{topic}"]]\n'
+ )
+
+
def edit_draft(ctx, dirname=None, draftno=None, text=None):
logging.debug(f"editing draft {draftno} in {dirname} to also contain {text!r}")
draft = os.path.join(dirname, "drafts", f"{draftno}.md")
diff --git a/subplot/jt.yaml b/subplot/jt.yaml
index 65bf90e..a3d4f0f 100644
--- a/subplot/jt.yaml
+++ b/subplot/jt.yaml
@@ -43,6 +43,9 @@
- then: draft {draftno} in {dirname} contains "{pattern:text}"
function: draft_contains_string
+- then: draft {draftno} in {dirname} links to "{topic}"
+ function: draft_links_to_topic
+
- then: there are no journal entries in {dirname}
function: journal_has_no_entries