use crate::*; macro_rules! get_subsection { ($t:ident) => { pub fn get_subsection(&self, name: &str) -> Option<&$t> { for section in &self.sections { if line_to_string(§ion.title) == name { return Some(section); } } return None; } }; } #[derive(Default)] pub struct Document { pub preamble: Vec, pub sections: Vec, } impl Document { get_subsection! {TopLevelSection} } #[derive(Default)] pub struct TopLevelSection { pub title: Line, pub content: Vec, pub sections: Vec, } impl TopLevelSection { get_subsection! {MidLevelSection} } #[derive(Default)] pub struct MidLevelSection { pub title: Line, pub content: Vec, pub sections: Vec, } impl MidLevelSection { get_subsection! {LowLevelSection} } #[derive(Default)] pub struct LowLevelSection { pub title: Line, pub content: Vec, } pub fn parse_heirarchical(markdown: &str) -> Result { macro_rules! push_section { ($from:ident => $to:ident) => { $to.sections.push(std::mem::take(&mut $from)) }; } let mut document = Document::default(); let mut h1_buffer = TopLevelSection::default(); let mut h2_buffer = MidLevelSection::default(); let mut h3_buffer = LowLevelSection::default(); let mut level = 0; let blocks = parse(markdown); for block in blocks { match (level, block) { (0, Block::Heading1(title)) => { h1_buffer.title = title; level = 1; } (0, Block::Heading2(_)) => return Err(()), (0, Block::Heading3(_)) => return Err(()), (0, block) => document.preamble.push(block), (1, Block::Heading1(title)) => { push_section!(h1_buffer => document); h1_buffer.title = title; } (1, Block::Heading2(title)) => { h2_buffer.title = title; level = 2; } (1, Block::Heading3(_)) => return Err(()), (1, block) => h1_buffer.content.push(block), (2, Block::Heading1(title)) => { push_section!(h2_buffer => h1_buffer); push_section!(h1_buffer => document); h1_buffer.title = title; level = 1; } (2, Block::Heading2(title)) => { push_section!(h2_buffer => h1_buffer); h2_buffer.title = title; } (2, Block::Heading3(title)) => { h3_buffer.title = title; level = 3; } (2, block) => h2_buffer.content.push(block), (3, Block::Heading1(title)) => { push_section!(h3_buffer => h2_buffer); push_section!(h2_buffer => h1_buffer); push_section!(h1_buffer => document); h1_buffer.title = title; level = 1; } (3, Block::Heading2(title)) => { push_section!(h3_buffer => h2_buffer); push_section!(h2_buffer => h1_buffer); h2_buffer.title = title; level = 2; } (3, Block::Heading3(title)) => { push_section!(h3_buffer => h2_buffer); h3_buffer.title = title; } (3, block) => h3_buffer.content.push(block), _ => unreachable!(), } } // Push all in-progress sections match level { 3 => { push_section!(h3_buffer => h2_buffer); push_section!(h2_buffer => h1_buffer); push_section!(h1_buffer => document); } 2 => { push_section!(h2_buffer => h1_buffer); push_section!(h1_buffer => document); } 1 => { push_section!(h1_buffer => document); } _ => (), } Ok(document) }