1use serde::{Deserialize, Serialize};
4use std::collections::VecDeque;
5use std::fmt::{self, Display, Formatter};
6use std::ops::{Deref, DerefMut};
7use std::path::PathBuf;
8
9#[cfg(test)]
10mod tests;
11
12#[allow(
22 clippy::exhaustive_structs,
23 reason = "This cannot be extended without breaking preprocessors."
24)]
25#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
26pub struct Book {
27 pub items: Vec<BookItem>,
29}
30
31impl Book {
32 pub fn new() -> Self {
34 Default::default()
35 }
36
37 pub fn new_with_items(items: Vec<BookItem>) -> Book {
39 Book { items }
40 }
41
42 pub fn iter(&self) -> BookItems<'_> {
44 BookItems {
45 items: self.items.iter().collect(),
46 }
47 }
48
49 pub fn chapters(&self) -> impl Iterator<Item = &Chapter> {
51 self.iter().filter_map(|item| match item {
52 BookItem::Chapter(ch) if !ch.is_draft_chapter() => Some(ch),
53 _ => None,
54 })
55 }
56
57 pub fn for_each_mut<F>(&mut self, mut func: F)
66 where
67 F: FnMut(&mut BookItem),
68 {
69 for_each_mut(&mut func, &mut self.items);
70 }
71
72 pub fn for_each_chapter_mut<F>(&mut self, mut func: F)
75 where
76 F: FnMut(&mut Chapter),
77 {
78 for_each_mut(
79 &mut |item| {
80 let BookItem::Chapter(ch) = item else {
81 return;
82 };
83 if ch.is_draft_chapter() {
84 return;
85 }
86 func(ch)
87 },
88 &mut self.items,
89 );
90 }
91
92 pub fn push_item<I: Into<BookItem>>(&mut self, item: I) -> &mut Self {
94 self.items.push(item.into());
95 self
96 }
97}
98
99fn for_each_mut<'a, F, I>(func: &mut F, items: I)
100where
101 F: FnMut(&mut BookItem),
102 I: IntoIterator<Item = &'a mut BookItem>,
103{
104 for item in items {
105 if let BookItem::Chapter(ch) = item {
106 for_each_mut(func, &mut ch.sub_items);
107 }
108
109 func(item);
110 }
111}
112
113#[allow(
115 clippy::exhaustive_enums,
116 reason = "This cannot be extended without breaking preprocessors."
117)]
118#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
119pub enum BookItem {
120 Chapter(Chapter),
122 Separator,
124 PartTitle(String),
126}
127
128impl From<Chapter> for BookItem {
129 fn from(other: Chapter) -> BookItem {
130 BookItem::Chapter(other)
131 }
132}
133
134#[allow(
137 clippy::exhaustive_structs,
138 reason = "This cannot be extended without breaking preprocessors."
139)]
140#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
141pub struct Chapter {
142 pub name: String,
144 pub content: String,
146 pub number: Option<SectionNumber>,
148 pub sub_items: Vec<BookItem>,
150 pub path: Option<PathBuf>,
158 pub source_path: Option<PathBuf>,
167 pub parent_names: Vec<String>,
169}
170
171impl Chapter {
172 pub fn new<P: Into<PathBuf>>(
174 name: &str,
175 content: String,
176 p: P,
177 parent_names: Vec<String>,
178 ) -> Chapter {
179 let path: PathBuf = p.into();
180 Chapter {
181 name: name.to_string(),
182 content,
183 path: Some(path.clone()),
184 source_path: Some(path),
185 parent_names,
186 ..Default::default()
187 }
188 }
189
190 pub fn new_draft(name: &str, parent_names: Vec<String>) -> Self {
193 Chapter {
194 name: name.to_string(),
195 content: String::new(),
196 path: None,
197 source_path: None,
198 parent_names,
199 ..Default::default()
200 }
201 }
202
203 pub fn is_draft_chapter(&self) -> bool {
205 self.path.is_none()
206 }
207}
208
209impl Display for Chapter {
210 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
211 if let Some(ref section_number) = self.number {
212 write!(f, "{section_number} ")?;
213 }
214
215 write!(f, "{}", self.name)
216 }
217}
218
219#[derive(Debug, PartialEq, Clone, Default, Serialize, Deserialize)]
222pub struct SectionNumber(Vec<u32>);
223
224impl SectionNumber {
225 pub fn new(numbers: impl Into<Vec<u32>>) -> SectionNumber {
227 SectionNumber(numbers.into())
228 }
229}
230
231impl Display for SectionNumber {
232 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
233 if self.0.is_empty() {
234 write!(f, "0")
235 } else {
236 for item in &self.0 {
237 write!(f, "{item}.")?;
238 }
239 Ok(())
240 }
241 }
242}
243
244impl Deref for SectionNumber {
245 type Target = Vec<u32>;
246 fn deref(&self) -> &Self::Target {
247 &self.0
248 }
249}
250
251impl DerefMut for SectionNumber {
252 fn deref_mut(&mut self) -> &mut Self::Target {
253 &mut self.0
254 }
255}
256
257impl FromIterator<u32> for SectionNumber {
258 fn from_iter<I: IntoIterator<Item = u32>>(it: I) -> Self {
259 SectionNumber(it.into_iter().collect())
260 }
261}
262
263pub struct BookItems<'a> {
270 items: VecDeque<&'a BookItem>,
271}
272
273impl<'a> Iterator for BookItems<'a> {
274 type Item = &'a BookItem;
275
276 fn next(&mut self) -> Option<Self::Item> {
277 let item = self.items.pop_front();
278
279 if let Some(BookItem::Chapter(ch)) = item {
280 for sub_item in ch.sub_items.iter().rev() {
282 self.items.push_front(sub_item);
283 }
284 }
285
286 item
287 }
288}