diff options
author | Lars Wirzenius <liw@liw.fi> | 2021-04-02 08:53:21 +0300 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2021-04-02 11:56:56 +0300 |
commit | 743d3c65cc69deb9fc4429190288dc8846bcd250 (patch) | |
tree | 7a643a7f530cef6b222d3d39a725d64970e4e65a /src | |
parent | 5aee9bd2ae4eb9c09e27c002f80d3edf071ebdcb (diff) | |
download | jt2-743d3c65cc69deb9fc4429190288dc8846bcd250.tar.gz |
feat! support multiple drafts
This changes the command line syntax: subcommands edit and finish now
require the draft id. We can change this later so that if there is
only one draft, the program picks that one automatically.
Also, new entries are 0.md, 1.md, etc, which is not going to be
acceptable for real use, but this works minimally.
Diffstat (limited to 'src')
-rw-r--r-- | src/bin/jt2.rs | 51 | ||||
-rw-r--r-- | src/error.rs | 20 | ||||
-rw-r--r-- | src/journal.rs | 108 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/opt.rs | 13 |
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(()) + } +} @@ -1,3 +1,4 @@ pub mod config; pub mod error; +pub mod journal; pub mod opt; @@ -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, + }, } |