summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDaniel Silverstone <dsilvers+gitlab@digital-scurf.org>2021-04-02 09:03:41 +0000
committerDaniel Silverstone <dsilvers+gitlab@digital-scurf.org>2021-04-02 09:03:41 +0000
commit575315c31c9c9ee5c5b7a0b8dea25e860a873752 (patch)
tree7a643a7f530cef6b222d3d39a725d64970e4e65a /src
parent33a8e6107f3282988db7dabd2f5f5eb407820561 (diff)
parent743d3c65cc69deb9fc4429190288dc8846bcd250 (diff)
downloadjt2-575315c31c9c9ee5c5b7a0b8dea25e860a873752.tar.gz
Merge branch 'easter' into 'main'
Debian packager and multi-draft support Closes #6 and #5 See merge request larswirzenius/jt!8
Diffstat (limited to 'src')
-rw-r--r--src/bin/jt2.rs51
-rw-r--r--src/error.rs20
-rw-r--r--src/journal.rs108
-rw-r--r--src/lib.rs1
-rw-r--r--src/opt.rs13
5 files changed, 156 insertions, 37 deletions
diff --git a/src/bin/jt2.rs b/src/bin/jt2.rs
index cf55aef..3d85229 100644
--- a/src/bin/jt2.rs
+++ b/src/bin/jt2.rs
@@ -1,11 +1,9 @@
use jt2::config::Configuration;
use jt2::error::JournalError;
+use jt2::journal::Journal;
use jt2::opt::{Opt, SubCommand};
-use log::debug;
-use std::fs;
use std::path::Path;
-use std::process::Command;
use structopt::StructOpt;
fn main() -> anyhow::Result<()> {
@@ -20,57 +18,40 @@ fn main() -> anyhow::Result<()> {
} => init(&config.dirname, &journalname, &description)?,
SubCommand::IsJournal => is_journal(&config.dirname)?,
SubCommand::New { title } => new_draft(&title, &config.dirname, &config.editor)?,
- SubCommand::Edit => edit_draft(&config.dirname, &config.editor)?,
- SubCommand::Finish => finish_draft(&config.dirname)?,
+ SubCommand::Edit { draft } => edit_draft(&config.dirname, &config.editor, &draft)?,
+ SubCommand::Finish { draft, basename } => finish_draft(&config.dirname, &draft, &basename)?,
}
Ok(())
}
fn init(dirname: &Path, _journalname: &str, _description: &str) -> anyhow::Result<()> {
- std::fs::create_dir(dirname)
- .map_err(|err| JournalError::CreateDirectory(dirname.to_path_buf(), err))?;
+ Journal::init(dirname)?;
Ok(())
}
fn is_journal(dirname: &Path) -> anyhow::Result<()> {
- let meta = fs::symlink_metadata(dirname)?;
- if !meta.is_dir() {
+ if !Journal::is_journal(dirname) {
return Err(JournalError::NotAJournal(dirname.display().to_string()).into());
}
Ok(())
}
-fn new_draft(title: &str, dirname: &Path, _editor: &str) -> anyhow::Result<()> {
- let drafts = dirname.join("drafts");
- if !drafts.exists() {
- std::fs::create_dir(&drafts)?;
- }
- let draft_filename = drafts.join("0.md");
- std::fs::write(draft_filename, title)?;
+fn new_draft(title: &str, dirname: &Path, editor: &str) -> anyhow::Result<()> {
+ let journal = Journal::new(dirname)?;
+ journal.new_draft(title, editor)?;
Ok(())
}
-fn edit_draft(dirname: &Path, editor: &str) -> anyhow::Result<()> {
- debug!("edit_draft: dirname={:?}", dirname);
- debug!("edit_draft: editor={:?}", editor);
- let drafts = dirname.join("drafts");
- let draft_filename = drafts.join("0.md");
- debug!("edit_draft: draft_filename={:?}", draft_filename);
- Command::new(editor).arg(draft_filename).status()?;
- debug!("edit_draft: editor finished");
+fn edit_draft(dirname: &Path, editor: &str, draft: &str) -> anyhow::Result<()> {
+ let journal = Journal::new(dirname)?;
+ let filename = journal.pick_draft(draft)?;
+ journal.edit_draft(editor, &filename)?;
Ok(())
}
-fn finish_draft(dirname: &Path) -> anyhow::Result<()> {
- let drafts = dirname.join("drafts");
- let draft = drafts.join("0.md");
-
- let entries = dirname.join("entries");
- if !entries.exists() {
- std::fs::create_dir(&entries)?;
- }
- let entry = entries.join("0.md");
-
- std::fs::rename(draft, entry)?;
+fn finish_draft(dirname: &Path, draft: &str, basename: &str) -> anyhow::Result<()> {
+ let journal = Journal::new(dirname)?;
+ let filename = journal.pick_draft(draft)?;
+ journal.finish_draft(&filename, basename)?;
Ok(())
}
diff --git a/src/error.rs b/src/error.rs
index 00dae56..232eaec 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -25,4 +25,24 @@ pub enum JournalError {
/// Failed to create the directory for the journal.
#[error("failed to create journal directory {0}")]
CreateDirectory(PathBuf, #[source] std::io::Error),
+
+ /// To many drafts.
+ #[error("there are already {0} drafts in {1}, can't create more")]
+ TooManyDrafts(usize, PathBuf),
+
+ /// User chose a draft that doesn't exist.
+ #[error("No draft {0} in {1}")]
+ NoSuchDraft(String, PathBuf),
+
+ /// Failed to read draft.
+ #[error("failed to drafts {0}: {1}")]
+ ReadDraft(PathBuf, #[source] std::io::Error),
+
+ /// Draft is not UTF8.
+ #[error("draft {0} is not UTF8: {1}")]
+ DraftNotUUtf8(PathBuf, #[source] std::string::FromUtf8Error),
+
+ /// Failed to get metadata for specific file in drafts folder.
+ #[error("failed to stat draft in {0}: {1}")]
+ StatDraft(PathBuf, #[source] std::io::Error),
}
diff --git a/src/journal.rs b/src/journal.rs
new file mode 100644
index 0000000..911f9a3
--- /dev/null
+++ b/src/journal.rs
@@ -0,0 +1,108 @@
+use crate::error::JournalError;
+use chrono::Local;
+use log::debug;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+
+const MAX_DRAFT_COUNT: usize = 1000;
+
+pub struct Journal {
+ dirname: PathBuf,
+}
+
+impl Journal {
+ pub fn is_journal(path: &Path) -> bool {
+ if let Ok(meta) = std::fs::symlink_metadata(path) {
+ meta.is_dir()
+ } else {
+ false
+ }
+ }
+
+ pub fn init(path: &Path) -> Result<Self, JournalError> {
+ std::fs::create_dir(path)
+ .map_err(|err| JournalError::CreateDirectory(path.to_path_buf(), err))?;
+ Ok(Self {
+ dirname: path.to_path_buf(),
+ })
+ }
+
+ pub fn new(path: &Path) -> Result<Self, JournalError> {
+ let dirname = path.to_path_buf();
+ if dirname.exists() {
+ Ok(Self { dirname })
+ } else {
+ Err(JournalError::NotAJournal(dirname.display().to_string()))
+ }
+ }
+
+ fn dirname(&self) -> &Path {
+ &self.dirname
+ }
+
+ fn drafts(&self) -> PathBuf {
+ self.dirname().join("drafts")
+ }
+
+ fn entries(&self) -> PathBuf {
+ self.dirname().join("entries")
+ }
+
+ pub fn new_draft(&self, title: &str, _editor: &str) -> anyhow::Result<()> {
+ let drafts = self.drafts();
+ if !drafts.exists() {
+ std::fs::create_dir(&drafts)?;
+ }
+
+ let pathname = self.pick_file_id(&drafts)?;
+ let text = format!(r#"[[!meta title="{}"]]"#, title);
+ std::fs::write(pathname, format!("{}\n\n", text))?;
+ Ok(())
+ }
+
+ fn pick_file_id(&self, dirname: &Path) -> Result<PathBuf, JournalError> {
+ for i in 0..MAX_DRAFT_COUNT {
+ let basename = format!("{}.md", i);
+ let pathname = dirname.join(basename);
+ if !pathname.exists() {
+ return Ok(pathname);
+ }
+ }
+ return Err(JournalError::TooManyDrafts(
+ MAX_DRAFT_COUNT,
+ dirname.to_path_buf(),
+ ));
+ }
+
+ pub fn pick_draft(&self, id: &str) -> Result<PathBuf, JournalError> {
+ let drafts = self.drafts();
+ let filename = drafts.join(format!("{}.md", id));
+ if filename.exists() {
+ return Ok(filename);
+ } else {
+ return Err(JournalError::NoSuchDraft(id.to_string(), self.drafts()));
+ }
+ }
+
+ pub fn edit_draft(&self, editor: &str, filename: &Path) -> anyhow::Result<()> {
+ debug!("edit_draft: editor={:?}", editor);
+ debug!("edit_draft: filename={:?}", filename);
+ Command::new(editor).arg(filename).status()?;
+ debug!("edit_draft: editor finished");
+ Ok(())
+ }
+
+ pub fn finish_draft(&self, filename: &Path, basename: &str) -> anyhow::Result<()> {
+ let entries = self.entries();
+ if !entries.exists() {
+ std::fs::create_dir(&entries)?;
+ }
+
+ let subdir = entries.join(Local::today().format("%Y/%m/%d").to_string());
+ std::fs::create_dir_all(&subdir)?;
+
+ let entry = subdir.join(basename);
+ std::fs::rename(filename, entry)?;
+ Ok(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 523317b..e988c12 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,3 +1,4 @@
pub mod config;
pub mod error;
+pub mod journal;
pub mod opt;
diff --git a/src/opt.rs b/src/opt.rs
index 12c3d26..df83659 100644
--- a/src/opt.rs
+++ b/src/opt.rs
@@ -63,8 +63,17 @@ pub enum SubCommand {
},
/// Invoke editor on journal entry draft.
- Edit,
+ Edit {
+ /// Draft id.
+ draft: String,
+ },
/// Finish a journal entry draft.
- Finish,
+ Finish {
+ /// Draft id.
+ draft: String,
+
+ /// Set base name of published file.
+ basename: String,
+ },
}