Repository: yshavit/mdq
Pull request #97 (2024-07-05 21:31:28)

flatten the MdElems tree
Inline containers and leaf blocks.

The more precise hierarchy was a nice idea, but it just adds a lot of extra boilerplate for no real benefit.

I thought this would be helpful for #74, but I don't think it actually will be. Still, I think it's an improvement, so I'm going to keep it.

A possible next step would be to create a new struct in `tree.rs`:

    pub struct Doc {
        pub body: Vec<MdElem>,
    }

But from trying it, it basically creates a lot more noise in the tests, and doesn't actually buy much in prod code -- so it's not worth it.


Patch:
diff --git a/src/fmt_md.rs b/src/fmt_md.rs
index 5f0c869..42fea62 100644
--- a/src/fmt_md.rs
+++ b/src/fmt_md.rs
@@ -560,7 +560,7 @@ pub mod tests {
         #[test]
         fn totally_empty() {
             check_render(
-                md_elems![Block::Container::Section {
+                md_elems![Section {
                     depth: 3,
                     title: vec![],
                     body: vec![],
@@ -573,7 +573,7 @@ pub mod tests {
         #[test]
         fn only_title() {
             check_render(
-                md_elems![Block::Container::Section {
+                md_elems![Section {
                     depth: 3,
                     title: vec![mdq_inline!("My header")],
                     body: vec![],
@@ -586,7 +586,7 @@ pub mod tests {
         #[test]
         fn only_body() {
             check_render(
-                md_elems![Block::Container::Section {
+                md_elems![Section {
                     depth: 3,
                     title: vec![],
                     body: md_elems!["Hello, world."],
@@ -601,10 +601,10 @@ pub mod tests {
         #[test]
         fn title_and_body() {
             check_render(
-                md_elems![Block::Container::Section {
+                md_elems![Section {
                     depth: 1,
                     title: vec![mdq_inline!("My title")],
-                    body: md_elems![Block::Container::BlockQuote {
+                    body: md_elems![BlockQuote {
                         body: md_elems!["Hello, world."],
                     },],
                 }],
@@ -636,7 +636,7 @@ pub mod tests {
         #[test]
         fn single_level() {
             check_render(
-                md_elems![Block::Container::BlockQuote {
+                md_elems![BlockQuote {
                     body: md_elems!["Hello, world"]
                 }],
                 indoc! {
@@ -648,10 +648,10 @@ pub mod tests {
         #[test]
         fn two_levels() {
             check_render(
-                md_elems![Block::Container::BlockQuote {
+                md_elems![BlockQuote {
                     body: md_elems![
                         "Outer",
-                        Block::Container::BlockQuote {
+                        BlockQuote {
                             body: md_elems!["Inner"],
                         },
                     ]
@@ -671,7 +671,7 @@ pub mod tests {
         #[test]
         fn ordered() {
             check_render(
-                md_elems![Block::Container::List {
+                md_elems![List {
                     starting_index: Some(3),
                     items: vec![
                         ListItem {
@@ -698,7 +698,7 @@ pub mod tests {
         #[test]
         fn unordered() {
             check_render(
-                md_elems![Block::Container::List {
+                md_elems![List {
                     starting_index: None,
                     items: vec![
                         ListItem {
@@ -725,7 +725,7 @@ pub mod tests {
         #[test]
         fn block_alignments() {
             check_render(
-                md_elems![Block::Container::List {
+                md_elems![List {
                     starting_index: None,
                     items: vec![
                         ListItem {
@@ -734,13 +734,13 @@ pub mod tests {
                         },
                         ListItem {
                             checked: None,
-                            item: md_elems!(Block::Container::BlockQuote {
+                            item: md_elems!(BlockQuote {
                                 body: md_elems!["quoted block one", "quoted block two"]
                             })
                         },
                         ListItem {
                             checked: None,
-                            item: md_elems!(Block::LeafBlock::CodeBlock {
+                            item: md_elems!(CodeBlock {
                                 variant: CodeVariant::Code(None),
                                 value: "line 1\nline 2".to_string(),
                             })
@@ -749,10 +749,10 @@ pub mod tests {
                             checked: Some(false),
                             item: md_elems![
                                 "closing argument",
-                                Block::Container::BlockQuote {
+                                BlockQuote {
                                     body: md_elems!["supporting evidence"]
                                 },
-                                Block::LeafBlock::CodeBlock {
+                                CodeBlock {
                                     variant: CodeVariant::Code(None),
                                     value: "line a\nline b".to_string(),
                                 },
@@ -832,7 +832,7 @@ pub mod tests {
         #[test]
         fn simple() {
             check_render(
-                md_elems![Block::LeafBlock::Table {
+                md_elems![Table {
                     alignments: vec![
                         mdast::AlignKind::Left,
                         mdast::AlignKind::Right,
@@ -869,7 +869,7 @@ pub mod tests {
         fn single_char_cells() {
             // This checks the minimum padding aspects
             check_render(
-                md_elems![Block::LeafBlock::Table {
+                md_elems![Table {
                     alignments: vec![
                         mdast::AlignKind::Left,
                         mdast::AlignKind::Right,
@@ -906,7 +906,7 @@ pub mod tests {
         fn empty_cells() {
             // This checks the minimum padding aspects
             check_render(
-                md_elems![Block::LeafBlock::Table {
+                md_elems![Table {
                     alignments: vec![
                         mdast::AlignKind::Left,
                         mdast::AlignKind::Right,
@@ -943,7 +943,7 @@ pub mod tests {
         fn row_counts_inconsistent() {
             // This is an invalid table, but we should still support it
             check_render(
-                md_elems![Block::LeafBlock::Table {
+                md_elems![Table {
                     alignments: vec![mdast::AlignKind::None, mdast::AlignKind::None,],
                     rows: vec![
                         // Header row: two values
@@ -980,17 +980,13 @@ pub mod tests {
 
         #[test]
         fn by_itself() {
-            check_render(vec![m_node!(MdElem::Block::LeafBlock::ThematicBreak)], "   -----");
+            check_render(vec![m_node!(MdElem::ThematicBreak)], "   -----");
         }
 
         #[test]
         fn with_paragraphs() {
             check_render(
-                vec![
-                    md_elem!("before"),
-                    m_node!(MdElem::Block::LeafBlock::ThematicBreak),
-                    md_elem!("after"),
-                ],
+                vec![md_elem!("before"), m_node!(MdElem::ThematicBreak), md_elem!("after")],
                 indoc! {r#"
                 before
 
@@ -1007,7 +1003,7 @@ pub mod tests {
         #[test]
         fn code_no_lang() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Code(None),
                     value: "one\ntwo".to_string(),
                 }],
@@ -1022,7 +1018,7 @@ pub mod tests {
         #[test]
         fn code_with_lang() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Code(Some(CodeOpts {
                         language: "rust".to_string(),
                         metadata: None
@@ -1040,7 +1036,7 @@ pub mod tests {
         #[test]
         fn code_with_lang_and_title() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Code(Some(CodeOpts {
                         language: "rust".to_string(),
                         metadata: Some(r#"title="my code""#.to_string())
@@ -1058,7 +1054,7 @@ pub mod tests {
         #[test]
         fn math_no_metadata() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Math { metadata: None },
                     value: "one\ntwo".to_string(),
                 }],
@@ -1073,7 +1069,7 @@ pub mod tests {
         #[test]
         fn math_with_metadata() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Math {
                         metadata: Some("metadata".to_string())
                     },
@@ -1090,7 +1086,7 @@ pub mod tests {
         #[test]
         fn toml() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Toml,
                     value: "one\ntwo".to_string(),
                 }],
@@ -1105,7 +1101,7 @@ pub mod tests {
         #[test]
         fn yaml() {
             check_render(
-                md_elems![Block::LeafBlock::CodeBlock {
+                md_elems![CodeBlock {
                     variant: CodeVariant::Yaml,
                     value: "one\ntwo".to_string(),
                 }],
@@ -1360,7 +1356,7 @@ pub mod tests {
                         ],
                         link_definition: link,
                     })),
-                    m_node!(MdElem::Block::LeafBlock::ThematicBreak),
+                    m_node!(MdElem::ThematicBreak),
                 ];
                 check_render(nodes, expect);
             }
@@ -1509,7 +1505,7 @@ pub mod tests {
                         alt: "hello _world_!".to_string(),
                         link,
                     })),
-                    m_node!(MdElem::Block::LeafBlock::ThematicBreak),
+                    m_node!(MdElem::ThematicBreak),
                 ];
                 check_render(nodes, expect);
             }
@@ -1733,7 +1729,7 @@ pub mod tests {
                         label: "a".to_string(),
                         text: md_elems!["Hello, world."],
                     })),
-                    m_node!(MdElem::Block::LeafBlock::ThematicBreak),
+                    m_node!(MdElem::ThematicBreak),
                 ],
                 indoc! {r#"
                     [^a]
@@ -1752,7 +1748,7 @@ pub mod tests {
                         label: "a".to_string(),
                         text: md_elems!["Hello,\nworld."],
                     })),
-                    m_node!(MdElem::Block::LeafBlock::ThematicBreak),
+                    m_node!(MdElem::ThematicBreak),
                 ],
                 indoc! {r#"
                     [^a]
@@ -1773,7 +1769,7 @@ pub mod tests {
         #[test]
         fn link_and_footnote() {
             check_render(
-                md_elems![Block::LeafBlock::Paragraph {
+                md_elems![Paragraph {
                     body: vec![
                         mdq_inline!("Hello, "),
                         m_node!(Inline::Link {
@@ -1869,7 +1865,7 @@ pub mod tests {
                         link_canonicalization: LinkTransform::Keep,
                     },
                 },
-                md_elems![Block::LeafBlock::Paragraph {
+                md_elems![Paragraph {
                     body: vec![m_node!(Inline::Link {
                         text: vec![mdq_inline!("link description")],
                         link_definition: LinkDefinition {
@@ -1958,7 +1954,7 @@ pub mod tests {
                     },
                 },
                 // Define them in the opposite order that we'd expect them
-                md_elems![Block::LeafBlock::Paragraph {
+                md_elems![Paragraph {
                     body: vec![
                         Inline::Footnote(Footnote {
                             label: "d".to_string(),
@@ -2000,10 +1996,10 @@ pub mod tests {
 
         fn link_and_footnote_markdown() -> Vec<MdElem> {
             md_elems![
-                Block::Container::Section {
+                Section {
                     depth: 1,
                     title: vec![mdq_inline!("First section")],
-                    body: md_elems![Block::LeafBlock::Paragraph {
+                    body: md_elems![Paragraph {
                         body: vec![
                             m_node!(Inline::Link {
                                 text: vec![mdq_inline!("link description")],
@@ -2022,7 +2018,7 @@ pub mod tests {
                         ],
                     }],
                 },
-                Block::Container::Section {
+                Section {
                     depth: 1,
                     title: vec![mdq_inline!("Second section")],
                     body: md_elems!["Second section contents."],
diff --git a/src/fmt_str.rs b/src/fmt_str.rs
index 2a5b45f..9f09e8d 100644
--- a/src/fmt_str.rs
+++ b/src/fmt_str.rs
@@ -36,7 +36,7 @@ mod tests {
     use super::*;
     use indoc::indoc;
 
-    use crate::tree::{Block, FormattingVariant, Inline, LeafBlock, MdElem, ReadOptions, TextVariant};
+    use crate::tree::{FormattingVariant, Inline, MdElem, ReadOptions, TextVariant};
     use crate::unwrap;
     use markdown::ParseOptions;
 
@@ -122,7 +122,7 @@ mod tests {
         options.constructs.math_text = true;
         let node = markdown::to_mdast(md, &options).unwrap();
         let md_elems = MdElem::read(node, &ReadOptions::default()).unwrap();
-        unwrap!(&md_elems[0], MdElem::Block(Block::LeafBlock(LeafBlock::Paragraph(p))));
+        unwrap!(&md_elems[0], MdElem::Paragraph(p));
         p.body.iter().for_each(|inline| VARIANTS_CHECKER.see(inline));
         let actual = inlines_to_plain_string(&p.body);
         assert_eq!(&actual, expect);
diff --git a/src/matcher.rs b/src/matcher.rs
index e5128e5..ca6567d 100644
--- a/src/matcher.rs
+++ b/src/matcher.rs
@@ -1,7 +1,7 @@
 use crate::fmt_str::inlines_to_plain_string;
 use crate::parsing_iter::ParsingIterator;
 use crate::select::{ParseErrorReason, ParseResult};
-use crate::tree::{Block, Container, Inline, LeafBlock, MdElem};
+use crate::tree::{Inline, MdElem};
 use regex::Regex;
 use std::borrow::Borrow;
 
@@ -49,11 +49,11 @@ impl StringMatcher {
         haystacks.iter().any(|node| self.matches_node(node.borrow()))
     }
 
-    fn matches_block(&self, block: &Block) -> bool {
-        match block {
-            Block::LeafBlock(LeafBlock::Paragraph(p)) => self.matches_inlines(&p.body),
-            Block::LeafBlock(LeafBlock::ThematicBreak | LeafBlock::CodeBlock(_)) => false,
-            Block::LeafBlock(LeafBlock::Table(table)) => {
+    fn matches_node(&self, node: &MdElem) -> bool {
+        match node {
+            MdElem::Paragraph(p) => self.matches_inlines(&p.body),
+            MdElem::ThematicBreak | MdElem::CodeBlock(_) => false,
+            MdElem::Table(table) => {
                 for row in &table.rows {
                     for cell in row {
                         if self.matches_inlines(cell) {
@@ -63,20 +63,14 @@ impl StringMatcher {
                 }
                 false
             }
-            Block::Container(Container::List(list)) => list.items.iter().any(|li| self.matches_any(&li.item)),
-            Block::Container(Container::BlockQuote(block)) => self.matches_any(&block.body),
-            Block::Container(Container::Section(section)) => {
+            MdElem::List(list) => list.items.iter().any(|li| self.matches_any(&li.item)),
+            MdElem::BlockQuote(block) => self.matches_any(&block.body),
+            MdElem::Section(section) => {
                 if self.matches_inlines(&section.title) {
                     return true;
                 }
                 self.matches_any(&section.body)
             }
-        }
-    }
-
-    fn matches_node(&self, node: &MdElem) -> bool {
-        match node {
-            MdElem::Block(block) => self.matches_block(block),
             MdElem::Inline(inline) => self.matches_inlines(&[inline]),
         }
     }
diff --git a/src/tree.rs b/src/tree.rs
index 1bc296d..1a0ddb4 100644
--- a/src/tree.rs
+++ b/src/tree.rs
@@ -8,29 +8,18 @@ use markdown::mdast;
 
 #[derive(Debug, PartialEq)]
 pub enum MdElem {
-    Block(Block),
-    Inline(Inline),
-}
-
-#[derive(Debug, PartialEq)]
-pub enum Block {
-    Container(Container),
-    LeafBlock(LeafBlock),
-}
+    // Container blocks
+    BlockQuote(BlockQuote),
+    List(List),
+    Section(Section),
 
-#[derive(Debug, PartialEq)]
-pub enum LeafBlock {
+    // Leaf blocks
     CodeBlock(CodeBlock),
     Paragraph(Paragraph),
     Table(Table),
     ThematicBreak,
-}
 
-#[derive(Debug, PartialEq)]
-pub enum Container {
-    BlockQuote(BlockQuote),
-    List(List),
-    Section(Section),
+    Inline(Inline),
 }
 
 #[derive(Debug, PartialEq)]
@@ -276,7 +265,7 @@ impl MdElem {
     fn from_mdast_0(node: mdast::Node, lookups: &Lookups) -> Result<Vec<Self>, InvalidMd> {
         let result = match node {
             mdast::Node::Root(node) => return MdElem::all(node.children, lookups),
-            mdast::Node::BlockQuote(node) => m_node!(MdElem::Block::Container::BlockQuote {
+            mdast::Node::BlockQuote(node) => m_node!(MdElem::BlockQuote {
                 body: MdElem::all(node.children, lookups)?,
             }),
             mdast::Node::FootnoteDefinition(_) => return Ok(Vec::new()),
@@ -292,7 +281,7 @@ impl MdElem {
                     };
                     li_nodes.push(li_mdq);
                 }
-                m_node!(MdElem::Block::Container::List {
+                m_node!(MdElem::List {
                     starting_index: node.start,
                     items: li_nodes,
                 })
@@ -358,7 +347,7 @@ impl MdElem {
             })),
             mdast::Node::Code(node) => {
                 let mdast::Code { value, lang, meta, .. } = node;
-                m_node!(MdElem::Block::LeafBlock::CodeBlock {
+                m_node!(MdElem::CodeBlock {
                     value,
                     variant: CodeVariant::Code(match lang {
                         None => None,
@@ -371,12 +360,12 @@ impl MdElem {
             }
             mdast::Node::Math(node) => {
                 let mdast::Math { value, meta, .. } = node;
-                m_node!(MdElem::Block::LeafBlock::CodeBlock {
+                m_node!(MdElem::CodeBlock {
                     value,
                     variant: CodeVariant::Math { metadata: meta },
                 })
             }
-            mdast::Node::Heading(node) => m_node!(MdElem::Block::Container::Section {
+            mdast::Node::Heading(node) => m_node!(MdElem::Section {
                 depth: node.depth,
                 title: Self::inlines(node.children, lookups)?,
                 body: Vec::new(),
@@ -401,24 +390,24 @@ impl MdElem {
                     }
                     rows.push(column);
                 }
-                m_node!(MdElem::Block::LeafBlock::Table {
+                m_node!(MdElem::Table {
                     alignments: align,
                     rows,
                 })
             }
-            mdast::Node::ThematicBreak(_) => m_node!(MdElem::Block::LeafBlock::ThematicBreak),
+            mdast::Node::ThematicBreak(_) => m_node!(MdElem::ThematicBreak),
             mdast::Node::TableRow(_) | mdast::Node::TableCell(_) | mdast::Node::ListItem(_) => {
                 return Err(InvalidMd::InternalError); // should have been handled by Node::Table
             }
             mdast::Node::Definition(_) => return Ok(Vec::new()),
-            mdast::Node::Paragraph(node) => m_node!(MdElem::Block::LeafBlock::Paragraph {
+            mdast::Node::Paragraph(node) => m_node!(MdElem::Paragraph {
                 body: Self::inlines(node.children, lookups)?,
             }),
-            mdast::Node::Toml(node) => m_node!(MdElem::Block::LeafBlock::CodeBlock {
+            mdast::Node::Toml(node) => m_node!(MdElem::CodeBlock {
                 variant: CodeVariant::Toml,
                 value: node.value,
             }),
-            mdast::Node::Yaml(node) => m_node!(MdElem::Block::LeafBlock::CodeBlock {
+            mdast::Node::Yaml(node) => m_node!(MdElem::CodeBlock {
                 variant: CodeVariant::Yaml,
                 value: node.value,
             }),
@@ -461,7 +450,7 @@ impl MdElem {
         let mut headers: Vec<HContainer> = Vec::with_capacity(result.capacity());
         for child_mdq in iter {
             let child_mdq = child_mdq?;
-            if let m_node!(MdElem::Block::Container::Section {
+            if let m_node!(MdElem::Section {
                 depth,
                 title,
                 body: children,
@@ -485,7 +474,7 @@ impl MdElem {
                         // to the new previous, or else to the top-level results if there is no new
                         // previous. Then, we'll just loop back around.
                         let HContainer { depth, title, children } = headers.pop().unwrap(); // "let Some(prev)" above guarantees that this works
-                        let prev = m_node!(MdElem::Block::Container::Section {
+                        let prev = m_node!(MdElem::Section {
                             depth,
                             title,
                             body: children,
@@ -511,7 +500,7 @@ impl MdElem {
 
         // At this point, we still have our last tree branch of headers. Fold it up into the results.
         while let Some(HContainer { depth, title, children }) = headers.pop() {
-            let mdq_header = m_node!(MdElem::Block::Container::Section {
+            let mdq_header = m_node!(MdElem::Section {
                 depth,
                 title,
                 body: children,
@@ -526,7 +515,7 @@ impl MdElem {
         headers
             .drain(..)
             .map(|HContainer { depth, title, children }| {
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth,
                     title,
                     body: children,
@@ -772,7 +761,7 @@ mod tests {
         fn block_quote() {
             let (root, lookups) = parse("> hello");
             let child = &root.children[0];
-            check!(child, Node::BlockQuote(_), lookups => m_node!(MdElem::Block::Container::BlockQuote{body}) = {
+            check!(child, Node::BlockQuote(_), lookups => m_node!(MdElem::BlockQuote{body}) = {
                 assert_eq!(body, md_elems!["hello"]);
             });
         }
@@ -811,7 +800,7 @@ mod tests {
                     assert_eq!(footnote, Inline::Footnote(Footnote{
                         label: "a".to_string(),
                         text: vec![
-                            m_node!(MdElem::Block::Container::List{
+                            m_node!(MdElem::List{
                                 starting_index: None,
                                 items: vec![
                                     ListItem{
@@ -845,7 +834,7 @@ mod tests {
             );
             assert_eq!(root.children.len(), 2); // unordered list, then ordered
 
-            check!(&root.children[0], Node::List(ul), lookups => m_node!(MdElem::Block::Container::List{starting_index, items}) = {
+            check!(&root.children[0], Node::List(ul), lookups => m_node!(MdElem::List{starting_index, items}) = {
                 for child in &ul.children {
                     check!(error: child, Node::ListItem(_), lookups => InvalidMd::InternalError);
                 }
@@ -865,7 +854,7 @@ mod tests {
                     },
                 ]);
             });
-            check!(&root.children[1], Node::List(ol), lookups => m_node!(MdElem::Block::Container::List{starting_index, items}) = {
+            check!(&root.children[1], Node::List(ol), lookups => m_node!(MdElem::List{starting_index, items}) = {
                 for child in &ol.children {
                     check!(error: child, Node::ListItem(_), lookups => InvalidMd::InternalError);
                 }
@@ -900,7 +889,7 @@ mod tests {
                 "#},
             );
 
-            check!(&root.children[0], Node::Paragraph(p), lookups => m_node!(MdElem::Block::LeafBlock::Paragraph{body}) = {
+            check!(&root.children[0], Node::Paragraph(p), lookups => m_node!(MdElem::Paragraph{body}) = {
                 assert_eq!(p.children.len(), 3);
                 check!(&p.children[0], Node::Text(_), lookups => MdElem::Inline(text) = {
                     assert_eq!(text, Inline::Text(Text{variant: TextVariant::Plain, value: "hello ".to_string()}));
@@ -1002,7 +991,7 @@ mod tests {
                 let (root, lookups) = parse(indoc! {r#"
                 In <em>a paragraph.</em>
                 "#});
-                check!(&root.children[0], Node::Paragraph(_), lookups => m_node!(MdElem::Block::LeafBlock::Paragraph{body}) = {
+                check!(&root.children[0], Node::Paragraph(_), lookups => m_node!(MdElem::Paragraph{body}) = {
                     assert_eq!(body.len(), 4);
                     assert_eq!(body, vec![
                         mdq_inline!("In "),
@@ -1022,7 +1011,7 @@ mod tests {
                 In <em
                 newline  >a paragraph.</em>
                 "#});
-                check!(&root.children[0], Node::Paragraph(_), lookups => m_node!(MdElem::Block::LeafBlock::Paragraph{body}) = {
+                check!(&root.children[0], Node::Paragraph(_), lookups => m_node!(MdElem::Paragraph{body}) = {
                     assert_eq!(body.len(), 4);
                     assert_eq!(body, vec![
                         mdq_inline!("In "),
@@ -1099,7 +1088,7 @@ mod tests {
             {
                 // This isn't an image, though it almost looks like one
                 let (root, lookups) = parse(r#"![]("only a tooltip")"#);
-                check!(&root.children[0], Node::Paragraph(_), lookups => p @ m_node!(MdElem::Block::LeafBlock::Paragraph{ .. }) = {
+                check!(&root.children[0], Node::Paragraph(_), lookups => p @ m_node!(MdElem::Paragraph{ .. }) = {
                     assert_eq!(p, md_elem!(r#"![]("only a tooltip")"#));
                 });
             }
@@ -1480,7 +1469,7 @@ mod tests {
                     plain code block
                     ```"#},
                 );
-                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     assert_eq!(variant, CodeVariant::Code(None));
                     assert_eq!(value, "plain code block");
                 })
@@ -1493,7 +1482,7 @@ mod tests {
                     code block with language
                     ```"#},
                 );
-                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     assert_eq!(variant, CodeVariant::Code(Some(CodeOpts{
                         language: "rust".to_string(),
                         metadata: None})));
@@ -1508,7 +1497,7 @@ mod tests {
                     code block with language and title
                     ```"#},
                 );
-                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     assert_eq!(variant, CodeVariant::Code(Some(CodeOpts{
                         language: "rust".to_string(),
                         metadata: Some(r#"title="example.rs""#.to_string())})));
@@ -1523,7 +1512,7 @@ mod tests {
                     code block with only title
                     ```"#},
                 );
-                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Code(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     // It's actually just a bogus language!
                     assert_eq!(variant, CodeVariant::Code(Some(CodeOpts{
                         language: r#"title="example.rs""#.to_string(),
@@ -1545,7 +1534,7 @@ mod tests {
                     x = {-b \pm \sqrt{b^2-4ac} \over 2a}
                     $$"#},
                 );
-                check!(&root.children[0], Node::Math(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Math(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     assert_eq!(variant, CodeVariant::Math{metadata: None});
                     assert_eq!(value, r#"x = {-b \pm \sqrt{b^2-4ac} \over 2a}"#);
                 })
@@ -1558,7 +1547,7 @@ mod tests {
                     x = {-b \pm \sqrt{b^2-4ac} \over 2a}
                     $$"#},
                 );
-                check!(&root.children[0], Node::Math(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+                check!(&root.children[0], Node::Math(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                     assert_eq!(variant, CodeVariant::Math{metadata: Some("my metadata".to_string())});
                     assert_eq!(value, r#"x = {-b \pm \sqrt{b^2-4ac} \over 2a}"#);
                 })
@@ -1576,7 +1565,7 @@ mod tests {
                 my: toml
                 +++"#},
             );
-            check!(&root.children[0], Node::Toml(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+            check!(&root.children[0], Node::Toml(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                 assert_eq!(variant, CodeVariant::Toml);
                 assert_eq!(value, r#"my: toml"#);
             })
@@ -1593,7 +1582,7 @@ mod tests {
                 my: toml
                 ---"#},
             );
-            check!(&root.children[0], Node::Yaml(_), lookups => m_node!(MdElem::Block::LeafBlock::CodeBlock{variant, value}) = {
+            check!(&root.children[0], Node::Yaml(_), lookups => m_node!(MdElem::CodeBlock{variant, value}) = {
                 assert_eq!(variant, CodeVariant::Yaml);
                 assert_eq!(value, r#"my: toml"#);
             })
@@ -1608,7 +1597,7 @@ mod tests {
                     And some text below it."#},
             );
 
-            let (header_depth, header_title) = check!(&root.children[0], Node::Heading(_), lookups => m_node!(MdElem::Block::Container::Section{depth, title, body}) = {
+            let (header_depth, header_title) = check!(&root.children[0], Node::Heading(_), lookups => m_node!(MdElem::Section{depth, title, body}) = {
                 assert_eq!(depth, 2);
                 assert_eq!(title, vec![
                     Inline::Text (Text{ variant: TextVariant::Plain, value: "Header with ".to_string()}),
@@ -1631,7 +1620,7 @@ mod tests {
 
             assert_eq!(
                 mdqs,
-                vec![m_node!(MdElem::Block::Container::Section {
+                vec![m_node!(MdElem::Section {
                     depth: header_depth,
                     title: header_title,
                     body: md_elems!["And some text below it."],
@@ -1653,7 +1642,7 @@ mod tests {
             );
 
             assert_eq!(root.children.len(), 3);
-            check!(&root.children[1], Node::ThematicBreak(_), lookups => m_node!(MdElem::Block::LeafBlock::ThematicBreak) = {
+            check!(&root.children[1], Node::ThematicBreak(_), lookups => m_node!(MdElem::ThematicBreak) = {
                 // nothing to check
             });
         }
@@ -1672,7 +1661,7 @@ mod tests {
                     "#},
             );
             assert_eq!(root.children.len(), 1);
-            check!(&root.children[0], Node::Table(table_node), lookups => m_node!(MdElem::Block::LeafBlock::Table{alignments, rows}) = {
+            check!(&root.children[0], Node::Table(table_node), lookups => m_node!(MdElem::Table{alignments, rows}) = {
                 assert_eq!(alignments, vec![mdast::AlignKind::Left, mdast::AlignKind::Center, mdast::AlignKind::Right, mdast::AlignKind::None]);
                 assert_eq!(rows,
                     vec![ // rows
@@ -1921,26 +1910,26 @@ mod tests {
         #[test]
         fn h1_with_two_paragraphs() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("first")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("aaa")],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("bbb")],
                 }),
             ];
-            let expect = vec![m_node!(MdElem::Block::Container::Section {
+            let expect = vec![m_node!(MdElem::Section {
                 depth: 1,
                 title: vec![mdq_inline!("first")],
                 body: vec![
-                    m_node!(MdElem::Block::LeafBlock::Paragraph {
+                    m_node!(MdElem::Paragraph {
                         body: vec![mdq_inline!("aaa")],
                     }),
-                    m_node!(MdElem::Block::LeafBlock::Paragraph {
+                    m_node!(MdElem::Paragraph {
                         body: vec![mdq_inline!("bbb")],
                     }),
                 ],
@@ -1953,27 +1942,27 @@ mod tests {
         #[test]
         fn simple_nesting() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("first")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("aaa")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("bbb")],
                 }),
             ];
-            let expect = vec![m_node!(MdElem::Block::Container::Section {
+            let expect = vec![m_node!(MdElem::Section {
                 depth: 1,
                 title: vec![mdq_inline!("first")],
-                body: vec![m_node!(MdElem::Block::Container::Section {
+                body: vec![m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("aaa")],
-                    body: vec![m_node!(MdElem::Block::LeafBlock::Paragraph {
+                    body: vec![m_node!(MdElem::Paragraph {
                         body: vec![mdq_inline!("bbb")],
                     })],
                 })],
@@ -1986,53 +1975,53 @@ mod tests {
         #[test]
         fn only_headers() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("first")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("second")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("third")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("fourth")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("fifth")],
                     body: vec![],
                 }),
             ];
-            let expect = vec![m_node!(MdElem::Block::Container::Section {
+            let expect = vec![m_node!(MdElem::Section {
                 depth: 1,
                 title: vec![mdq_inline!("first")],
                 body: vec![
-                    m_node!(MdElem::Block::Container::Section {
+                    m_node!(MdElem::Section {
                         depth: 2,
                         title: vec![mdq_inline!("second")],
                         body: vec![
-                            m_node!(MdElem::Block::Container::Section {
+                            m_node!(MdElem::Section {
                                 depth: 3,
                                 title: vec![mdq_inline!("third")],
                                 body: vec![],
                             }),
-                            m_node!(MdElem::Block::Container::Section {
+                            m_node!(MdElem::Section {
                                 depth: 3,
                                 title: vec![mdq_inline!("fourth")],
                                 body: vec![],
                             }),
                         ],
                     }),
-                    m_node!(MdElem::Block::Container::Section {
+                    m_node!(MdElem::Section {
                         depth: 2,
                         title: vec![mdq_inline!("fifth")],
                         body: vec![],
@@ -2047,18 +2036,18 @@ mod tests {
         #[test]
         fn no_headers() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("one")],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("two")],
                 }),
             ];
             let expect = vec![
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("one")],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("two")],
                 }),
             ];
@@ -2070,40 +2059,40 @@ mod tests {
         #[test]
         fn header_skips() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("one")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 5,
                     title: vec![mdq_inline!("five")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("two")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("three")],
                     body: vec![],
                 }),
             ];
-            let expect = vec![m_node!(MdElem::Block::Container::Section {
+            let expect = vec![m_node!(MdElem::Section {
                 depth: 1,
                 title: vec![mdq_inline!("one")],
                 body: vec![
-                    m_node!(MdElem::Block::Container::Section {
+                    m_node!(MdElem::Section {
                         depth: 5,
                         title: vec![mdq_inline!("five")],
                         body: vec![],
                     }),
-                    m_node!(MdElem::Block::Container::Section {
+                    m_node!(MdElem::Section {
                         depth: 2,
                         title: vec![mdq_inline!("two")],
-                        body: vec![m_node!(MdElem::Block::Container::Section {
+                        body: vec![m_node!(MdElem::Section {
                             depth: 3,
                             title: vec![mdq_inline!("three")],
                             body: vec![],
@@ -2119,34 +2108,34 @@ mod tests {
         #[test]
         fn backwards_order() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("three")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("two")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("one")],
                     body: vec![],
                 }),
             ];
             let expect = vec![
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("three")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 2,
                     title: vec![mdq_inline!("two")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 1,
                     title: vec![mdq_inline!("one")],
                     body: vec![],
@@ -2160,26 +2149,26 @@ mod tests {
         #[test]
         fn paragraph_before_and_after_header() -> Result<(), InvalidMd> {
             let linear = vec![
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("before")],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("the header")],
                     body: vec![],
                 }),
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("after")],
                 }),
             ];
             let expect = vec![
-                m_node!(MdElem::Block::LeafBlock::Paragraph {
+                m_node!(MdElem::Paragraph {
                     body: vec![mdq_inline!("before")],
                 }),
-                m_node!(MdElem::Block::Container::Section {
+                m_node!(MdElem::Section {
                     depth: 3,
                     title: vec![mdq_inline!("the header")],
-                    body: vec![m_node!(MdElem::Block::LeafBlock::Paragraph {
+                    body: vec![m_node!(MdElem::Paragraph {
                         body: vec![mdq_inline!("after")],
                     })],
                 }),
diff --git a/src/tree_ref.rs b/src/tree_ref.rs
index 787ad48..19622a7 100644
--- a/src/tree_ref.rs
+++ b/src/tree_ref.rs
@@ -1,7 +1,4 @@
-use crate::tree::{
-    Block, BlockQuote, CodeBlock, Container, Image, Inline, LeafBlock, Link, List, ListItem, MdElem, Paragraph,
-    Section, Table,
-};
+use crate::tree::{BlockQuote, CodeBlock, Image, Inline, Link, List, ListItem, MdElem, Paragraph, Section, Table};
 
 /// An MdqNodeRef is a slice into an MdqNode tree, where each element can be outputted, and certain elements can be
 /// selected.
@@ -32,19 +29,13 @@ pub struct ListItemRef<'a>(pub Option<u32>, pub &'a ListItem);
 impl<'a> From<&'a MdElem> for MdElemRef<'a> {
     fn from(value: &'a MdElem) -> Self {
         match value {
-            MdElem::Block(block) => match block {
-                Block::LeafBlock(leaf) => match leaf {
-                    LeafBlock::ThematicBreak => Self::ThematicBreak,
-                    LeafBlock::Paragraph(p) => Self::Paragraph(p),
-                    LeafBlock::CodeBlock(c) => Self::CodeBlock(c),
-                    LeafBlock::Table(t) => Self::Table(t),
-                },
-                Block::Container(container) => match container {
-                    Container::List(list) => Self::List(list),
-                    Container::BlockQuote(block) => Self::BlockQuote(block),
-                    Container::Section(section) => Self::Section(section),
-                },
-            },
+            MdElem::ThematicBreak => Self::ThematicBreak,
+            MdElem::Paragraph(p) => Self::Paragraph(p),
+            MdElem::CodeBlock(c) => Self::CodeBlock(c),
+            MdElem::Table(t) => Self::Table(t),
+            MdElem::List(list) => Self::List(list),
+            MdElem::BlockQuote(block) => Self::BlockQuote(block),
+            MdElem::Section(section) => Self::Section(section),
             MdElem::Inline(child) => MdElemRef::Inline(child),
         }
     }


Test patch:
diff --git a/src/tree_test_utils.rs b/src/tree_test_utils.rs
index 4e7e0dd..3c1bf2e 100644
--- a/src/tree_test_utils.rs
+++ b/src/tree_test_utils.rs
@@ -6,7 +6,7 @@ mod test_utils {
             crate::m_node!(MdElem::$($node_names)::* {$($attr: $val),*})
         };
         ($paragraph_text:literal) => {
-            crate::m_node!(MdElem::Block::LeafBlock::Paragraph{body: vec![crate::mdq_inline!($paragraph_text)]})
+            crate::m_node!(MdElem::Paragraph{body: vec![crate::mdq_inline!($paragraph_text)]})
         };
     }