mdbook_journal/mdbook/preprocessor/
simple_directory.rs1use 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}