mdbook_journal/journal/
topic.rs

1use crate::prelude::*;
2
3mod builder;
4mod map;
5mod path_mapping;
6mod template;
7mod traits;
8mod variables;
9
10use crate::mdbook::preprocessor::DirectoryTemplate;
11use path_mapping::PathMapping;
12use template::Template;
13
14pub mod cli_entry;
15pub mod json_entry;
16
17pub use builder::*;
18pub use map::*;
19pub use traits::*;
20pub use variables::*;
21
22pub type TopicName = Arc<str>;
23
24/// Journal Topic
25///
26/// A topic represents a collection of similar entries
27/// in a journal.
28///
29#[derive(Debug)]
30pub struct Topic {
31    /// unique string that identifies this topic
32    name: TopicName,
33    /// root location in the generated mdbook where
34    /// this topic builds generated entries
35    virtual_root: PathBuf,
36    /// location relative to the mdbook `SUMMARY.md`
37    /// where topic entries are saved on the disk.
38    source_root: PathBuf,
39    /// describes data that will be collected for
40    /// a freshly created `Entry`
41    variables: VariableMap,
42    /// contains the logic for mapping an [Entry] to
43    /// a specific file
44    path_mapping: PathMapping,
45    /// template used for the initial content when
46    /// creating a new [Entry]
47    content_template: Template,
48    /// template used for a directory that contains
49    /// entries only
50    leaf_template: DirectoryTemplate,
51}
52
53impl Topic {
54    pub(crate) fn builder<S>(name: S) -> TopicBuilder
55    where
56        S: Into<String>,
57    {
58        TopicBuilder::new(name)
59    }
60
61    pub fn virtual_path(&self, entry: &Entry) -> Result<PathBuf> {
62        Ok(self.virtual_root.join(self.path_mapping.map(entry)?))
63    }
64
65    pub fn source_path(&self, entry: &Entry) -> Result<PathBuf> {
66        Ok(self.source_root.join(self.path_mapping.map(entry)?))
67    }
68
69    pub fn name(&self) -> &str {
70        self.name.as_ref()
71    }
72
73    pub fn generate_entry(&self, adapter: &dyn EntryGenerationTrait) -> Result<Entry> {
74        let mut entry = Entry::builder(self.name());
75        entry = entry.created_at(adapter.created_at()?);
76
77        for var in self.variables.iter() {
78            match adapter.collect_value(var)? {
79                Some(value) => {
80                    entry = entry.add_meta_value(var.key(), value);
81                }
82                None if var.is_required() => {
83                    if let Some(value) = var.default_value() {
84                        entry = entry.add_meta_value(var.key(), value);
85                    } else {
86                        bail!("{} is required", var.key())
87                    }
88                }
89                None => continue,
90            }
91        }
92
93        let content = self.content_template.generate_content(entry.as_ref())?;
94        let entry = entry.content(content);
95        Ok(entry.build())
96    }
97
98    pub fn directory_template(&self) -> &DirectoryTemplate {
99        &self.leaf_template
100    }
101
102    pub fn source_root(&self) -> &PathBuf {
103        &self.source_root
104    }
105
106    pub fn virtual_root(&self) -> &PathBuf {
107        &self.virtual_root
108    }
109}