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
use crate::prelude::*;

mod builder;
mod map;
mod path_mapping;
mod template;
mod traits;
mod variables;

use path_mapping::PathMapping;
use template::Template;

pub mod cli_entry;

pub use builder::*;
pub use map::*;
pub use traits::*;
pub use variables::*;

pub type TopicName = Arc<str>;

/// Journal Topic
///
/// A topic represents a collection of similar entries
/// in a journal.
///
#[derive(Debug)]
pub struct Topic {
    /// unique string that identifies this topic
    name: TopicName,
    /// root location in the generated mdbook where
    /// this topic builds generated entries
    virtual_root: PathBuf,
    /// location relative to the mdbook `SUMMARY.md`
    /// where topic entries are saved on the disk.
    source_root: PathBuf,
    /// describes data that will be collected for
    /// a freshly created `Entry`
    variables: VariableMap,
    /// contains the logic for mapping an `Entry` to
    /// a specific file
    path_mapping: PathMapping,
    template: Template,
}

impl Topic {
    pub(crate) fn builder<S>(name: S) -> TopicBuilder
    where
        S: Into<String>,
    {
        TopicBuilder::new(name)
    }

    pub fn virtual_path(&self, entry: &Entry) -> Result<PathBuf> {
        Ok(self.virtual_root.join(self.path_mapping.map(entry)?))
    }

    pub fn source_path(&self, entry: &Entry) -> Result<PathBuf> {
        Ok(self.source_root.join(self.path_mapping.map(entry)?))
    }

    pub fn name(&self) -> &str {
        self.name.as_ref()
    }

    pub fn generate_entry<A>(&self, adapter: A) -> Result<Entry>
    where
        A: EntryGenerationTrait,
    {
        let mut entry = Entry::builder(self.name());
        entry = entry.created_at(adapter.created_at()?);

        for var in self.variables.iter() {
            match adapter.collect_value(var)? {
                Some(value) => {
                    entry = entry.add_meta_value(var.key(), value);
                }
                None if var.is_required() => {
                    if let Some(value) = var.default_value() {
                        entry = entry.add_meta_value(var.key(), value);
                    } else {
                        bail!("{} is required", var.key())
                    }
                }
                None => continue,
            }
        }

        let content = self.template.generate_content(entry.as_ref())?;
        let entry = entry.content(content);
        Ok(entry.build())
    }

    pub fn source_root(&self) -> &PathBuf {
        &self.source_root
    }

    pub fn virtual_root(&self) -> &PathBuf {
        &self.virtual_root
    }
}