1 //! Table-of-contents creation.
4 use std::string::String;
6 /// A (recursive) table of contents
9 /// The levels are strictly decreasing, i.e.
11 /// entries[0].level >= entries[1].level >= ...
13 /// Normally they are equal, but can differ in cases like A and B,
14 /// both of which end up in the same `Toc` as they have the same
22 entries: Vec<TocEntry>
26 fn count_entries_with_level(&self, level: u32) -> usize {
27 self.entries.iter().filter(|e| e.level == level).count()
40 /// Progressive construction of a table of contents.
42 pub struct TocBuilder {
44 /// The current hierarchy of parent headings, the levels are
45 /// strictly increasing (i.e., chain[0].level < chain[1].level <
46 /// ...) with each entry being the most recent occurrence of a
47 /// heading with that level (it doesn't include the most recent
48 /// occurrences of every level, just, if it *is* in `chain` then
49 /// it is the most recent one).
51 /// We also have `chain[0].level <= top_level.entries[last]`.
56 pub fn new() -> TocBuilder {
57 TocBuilder { top_level: Toc { entries: Vec::new() }, chain: Vec::new() }
61 /// Converts into a true `Toc` struct.
62 pub fn into_toc(mut self) -> Toc {
63 // we know all levels are >= 1.
68 /// Collapse the chain until the first heading more important than
69 /// `level` (i.e., lower level)
84 /// If we are considering H (i.e., level 3), then A and B are in
85 /// self.top_level, D is in C.children, and C, E, F, G are in
88 /// When we attempt to push H, we realize that first G is not the
89 /// parent (level is too high) so it is popped from chain and put
90 /// into F.children, then F isn't the parent (level is equal, aka
91 /// sibling), so it's also popped and put into E.children.
93 /// This leaves us looking at E, which does have a smaller level,
94 /// and, by construction, it's the most recent thing with smaller
95 /// level, i.e., it's the immediate parent of H.
96 fn fold_until(&mut self, level: u32) {
99 match self.chain.pop() {
101 this.map(|e| next.children.entries.push(e));
102 if next.level < level {
103 // this is the parent we want, so return it to
104 // its rightful place.
105 self.chain.push(next);
112 this.map(|e| self.top_level.entries.push(e));
119 /// Push a level `level` heading into the appropriate place in the
120 /// hierarchy, returning a string containing the section number in
121 /// `<num>.<num>.<num>` format.
122 pub fn push(&mut self, level: u32, name: String, id: String) -> &str {
125 // collapse all previous sections into their parents until we
126 // get to relevant heading (i.e., the first one with a smaller
128 self.fold_until(level);
132 let (toc_level, toc) = match self.chain.last() {
134 sec_number = String::new();
138 sec_number = entry.sec_number.clone();
139 sec_number.push_str(".");
140 (entry.level, &entry.children)
143 // fill in any missing zeros, e.g., for
146 for _ in toc_level..level - 1 {
147 sec_number.push_str("0.");
149 let number = toc.count_entries_with_level(level);
150 sec_number.push_str(&(number + 1).to_string())
153 self.chain.push(TocEntry {
158 children: Toc { entries: Vec::new() }
161 // get the thing we just pushed, so we can borrow the string
162 // out of it with the right lifetime
163 let just_inserted = self.chain.last_mut().unwrap();
164 &just_inserted.sec_number
168 impl fmt::Debug for Toc {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 fmt::Display::fmt(self, f)
174 impl fmt::Display for Toc {
175 fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
176 write!(fmt, "<ul>")?;
177 for entry in &self.entries {
178 // recursively format this table of contents (the
179 // `{children}` is the key).
181 "\n<li><a href=\"#{id}\">{num} {name}</a>{children}</li>",
183 num = entry.sec_number, name = entry.name,
184 children = entry.children)?