summaryrefslogtreecommitdiff
path: root/src/journal.rs
blob: 911f9a3a6b53a2dfd75f648f20c5d35e007c06bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
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(())
    }
}