use anyhow::Result; use log::debug; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; use structopt::StructOpt; use thiserror::Error; #[derive(Debug, StructOpt)] #[structopt(about = "maintain a journal")] enum JT { Init { #[structopt(help = "Directory where journal should be stored")] dirname: PathBuf, #[structopt(help = "Short name for journal")] journalname: String, #[structopt(help = "Short description of journal, its title")] description: String, }, IsJournal { #[structopt(help = "Directory that may or may not be a journal")] dirname: PathBuf, }, New { #[structopt(long, short, help = "Use DIRNAME as the location of the journal")] dirname: PathBuf, #[structopt( long, short, help = "Invoke EDITOR for user to edit draft", default_value = "/usr/bin/editor" )] editor: String, #[structopt(help = "Title of new draft")] title: String, }, Edit { #[structopt(long, short, help = "Use DIRNAME as the location of the journal")] dirname: PathBuf, #[structopt( long, short, help = "Invoke EDITOR for user to edit draft", default_value = "/usr/bin/editor" )] editor: String, }, Finish { #[structopt(long, short, help = "Use DIRNAME as the location of the journal")] dirname: PathBuf, }, } #[derive(Debug, Error)] enum JournalError { #[error("directory {0} is not a journal")] NotAJournal(String), } fn main() -> Result<()> { pretty_env_logger::init_custom_env("JT_LOG"); let opt = JT::from_args(); match opt { JT::Init { dirname, journalname, description, } => init(&dirname, &journalname, &description)?, JT::IsJournal { dirname } => is_journal(&dirname)?, JT::New { title, dirname, editor, } => new_draft(&title, &dirname, &editor)?, JT::Edit { dirname, editor } => edit_draft(&dirname, &editor)?, JT::Finish { dirname } => finish_draft(&dirname)?, } Ok(()) } fn init(dirname: &Path, _journalname: &str, _description: &str) -> anyhow::Result<()> { std::fs::create_dir(dirname)?; Ok(()) } fn is_journal(dirname: &Path) -> anyhow::Result<()> { let meta = fs::symlink_metadata(dirname)?; if !meta.is_dir() { 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)?; 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); let status = Command::new(editor).arg(draft_filename).status()?; debug!("edit_draft: editor finished"); 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)?; Ok(()) }