mdbook_journal/mdbook/preprocessor/
simple_directory.rs

1use super::prelude::*;
2use crate::prelude::*;
3
4mod directory_template;
5
6use crate::index::DirIndex;
7use crate::mdbook::traits::*;
8pub use directory_template::*;
9
10pub struct SimpleDirPreprocessor<T>
11where
12    T: JournalLoaderTrait,
13{
14    journal: Journal<T>,
15}
16
17impl<T> SimpleDirPreprocessor<T>
18where
19    T: JournalLoaderTrait,
20{
21    pub fn new<J>(journal: J) -> Self
22    where
23        J: Into<Journal<T>>,
24    {
25        Self {
26            journal: journal.into(),
27        }
28    }
29}
30
31impl<T> Preprocessor for SimpleDirPreprocessor<T>
32where
33    T: JournalLoaderTrait,
34{
35    fn name(&self) -> &str {
36        "Simple Directory Preprocessor"
37    }
38
39    fn run(&self, _ctx: &PreprocessorContext, mut book: Book) -> Result<Book> {
40        let journal = &self.journal;
41        let writing = &mut book;
42        let mut section = writing
43            .max_section_number()
44            .and_then(|s| s.root())
45            .unwrap_or_default();
46
47        for topic in journal.each_topic() {
48            let mut entries = journal.entries_for_topic(&topic.name())?;
49            entries.sort_by(|a, b| b.created_at().cmp(a.created_at()));
50            section.increment();
51            let chapter = topic_chapter(topic, &entries, section.clone())?;
52            writing.push_item(chapter);
53        }
54
55        Ok(book)
56    }
57}
58
59fn topic_chapter(topic: &Topic, entries: &[Entry], section: SectionNumber) -> Result<BookItem> {
60    let index = DirIndex::for_topic(entries, topic)?;
61    let parents = vec![topic.name().to_owned()];
62    let mut path = topic.virtual_root().clone();
63    path.push("README.md");
64    let sub_items = if index.is_empty() {
65        vec![]
66    } else {
67        build_sub_items(
68            topic,
69            &index[topic.name()],
70            parents,
71            section.advance_level(),
72        )?
73    };
74
75    Ok(BookItem::Chapter(Chapter {
76        sub_items,
77        name: topic.name().to_owned(),
78        path: Some(path),
79        number: Some(section),
80        ..Default::default()
81    }))
82}
83
84fn build_sub_items(
85    topic: &Topic,
86    index: &DirIndex,
87    parents: Vec<String>,
88    mut section: SectionNumber,
89) -> Result<Vec<BookItem>> {
90    Ok(if index.is_leaf() {
91        index
92            .entries()
93            .map(|entry| {
94                section.increment();
95                entry_chapter(topic, entry, parents.clone(), section.clone())
96            })
97            .collect()
98    } else {
99        index
100            .children()
101            .try_fold(Vec::new(), |mut vec, (name, dir)| {
102                let name = name.to_owned();
103                let mut new_parents = parents.clone();
104                new_parents.push(name.clone());
105                let mut path: PathBuf = new_parents.join("/").into();
106                section.increment();
107                path.push("README.md");
108                let content = build_content(dir, &path, topic)?;
109                vec.push(BookItem::Chapter(Chapter {
110                    sub_items: build_sub_items(topic, dir, new_parents, section.advance_level())?,
111                    parent_names: parents.clone(),
112                    number: Some(section.clone()),
113                    name,
114                    path: Some(path),
115                    content,
116                    ..Default::default()
117                }));
118                Ok(vec)
119            })?
120    })
121}
122
123fn build_content(dir: &DirIndex, path: &Path, topic: &Topic) -> Result<String> {
124    if !dir.is_leaf() {
125        return Ok(String::new());
126    }
127    let entries: Vec<_> = dir.entries().collect();
128    let data = &serde_json::json!({
129        "path": path,
130        "entries": entries,
131    });
132    topic
133        .directory_template()
134        .generate_content(data)
135        .with_context(|| format!("Generating contant with data:\n{data:#?}"))
136}
137
138fn entry_chapter(
139    topic: &Topic,
140    entry: &Entry,
141    parents: Vec<String>,
142    section: SectionNumber,
143) -> BookItem {
144    let name = match entry.meta_value(&"title") {
145        Some(MetaValue::String(title)) => title.to_owned(),
146        _ => String::from("Untitled"),
147    };
148    let content = entry.content().to_owned();
149
150    BookItem::Chapter(Chapter {
151        name,
152        content,
153        number: Some(section),
154        path: topic.virtual_path(entry).ok(),
155        source_path: entry.file_location().cloned(),
156        parent_names: parents,
157        ..Default::default()
158    })
159}