Some text.
Some more text.
Some more text.
Some more text.
And fin.
| // The Rust logic that's supposed to build `toc_result.rs` out of `example.md` but doesn't (yet) | |
| use markdown_it::{plugins::cmark::block::heading::ATXHeading, Node}; | |
| use serde::Serialize; | |
| use slug::slugify; | |
| // Recursive TOC struct | |
| #[derive(Debug, PartialEq, Serialize)] | |
| pub struct TableOfContents(Vec<(Heading, TableOfContents)>); | |
| // Initializer. Pass in an AST and a heading level to start (level 2 is standard). | |
| impl TableOfContents { | |
| pub fn new(document: &Node) -> Self { | |
| toc_for_level(&document.children, 2) | |
| } | |
| } | |
| // Create a table of contents for a specific heading level | |
| fn toc_for_level(nodes: &[Node], level: u8) -> TableOfContents { | |
| let mut toc: Vec<(Heading, TableOfContents)> = Vec::new(); | |
| // Iterate through each node in the provided portion of the AST | |
| for (idx, node) in nodes.iter().enumerate() { | |
| // Stop at each heading | |
| if let Some(h) = node.cast::<ATXHeading>() { | |
| // Stop at headings that match the desired level | |
| if h.level == level { | |
| // Convert the content of the heading to a string | |
| let text = &node_to_string(node); | |
| // Create a new heading to add to the TOC | |
| let heading = Heading::new(level, text); | |
| // Add the new heading to the mutable vector, and pass in the TOC | |
| // for the next heading level down (h2 to h3, h3 to h4, etc.) by | |
| // passing in the remaining nodes in the AST. This is the part that | |
| // doesn't work, because passing &nodes[idx..] here means that the | |
| // whole rest of the document is iterated over, not just the portion | |
| // that's underneath this heading. But I can't figure out a good way | |
| // to tell the function where to stop! Any help would be greatly | |
| // appreciated! 💯❤️ | |
| toc.push((heading, toc_for_level(&nodes[idx..], level + 1))); | |
| } | |
| } | |
| } | |
| TableOfContents(toc) | |
| } | |
| // Heading struct (for use in the TOC) | |
| #[derive(Debug, PartialEq, Serialize)] | |
| struct Heading { | |
| level: u8, | |
| text: String, | |
| slug: String, | |
| } | |
| // Heading initializer | |
| impl Heading { | |
| fn new(level: u8, text: &str) -> Self { | |
| let slug = slugify(text); | |
| Self { | |
| level, | |
| text: String::from(text), | |
| slug, | |
| } | |
| } | |
| } |
| // Desired result struct from parsing `example.md` | |
| TableOfContents(vec![ | |
| ( | |
| Heading { | |
| level: 2, | |
| text: String::from("Here's a level 2 header"), | |
| slug: String::from("here-s-a-level-2-header"), | |
| }, | |
| TableOfContents(vec![ | |
| ( | |
| Heading: { | |
| level: 3, | |
| text: String::from("Here's a level 3 header"), | |
| slug: String::from("here-s-a-level-3-header"), | |
| }, | |
| TableOfContents(vec![]) | |
| ) | |
| ]), | |
| ), | |
| ( | |
| Heading { | |
| level: 2, | |
| text: String::from("Back to level 2"), | |
| slug: String::from("back-to-level-2"), | |
| }, | |
| TableOfContents(vec![ | |
| ( | |
| Heading { | |
| level: 3, | |
| text: String::from("Going deeper"), | |
| slug: String::from("going-deeper"), | |
| }, | |
| TableOfContents(vec![ | |
| ( | |
| Heading { | |
| level: 4, | |
| text: String::from("Going yet deeper"), | |
| slug: String::from("going-yet-deeper"), | |
| }, | |
| TableOfContents(vec![]), | |
| ) | |
| ]), | |
| ), | |
| ]), | |
| ), | |
| ]) |