1mod heading;
2mod state;
3
4use heading::try_parse_heading;
5use regex::Regex;
6use state::State;
7use std::fs::File;
8use std::io::prelude::*;
9use std::io::BufReader;
10use std::path::PathBuf;
11
12pub type MarkdownSection = Vec<String>;
13
14pub fn extract_from_path(
15 path: &PathBuf,
16 regex: &Regex,
17) -> Result<Vec<MarkdownSection>, std::io::Error> {
18 let file = File::open(&path)?;
19 let mut reader = BufReader::new(file);
20 return Ok(extract_from_reader(&mut reader, ®ex));
21}
22
23pub fn extract_from_reader<R: Read>(
24 reader: &mut BufReader<R>,
25 regex: &Regex,
26) -> Vec<MarkdownSection> {
27 let mut state = State::default();
28
29 for line in reader.lines() {
30 let line = line.unwrap();
31
32 if line.starts_with("```") {
33 state.is_inside_code_block = !state.is_inside_code_block;
34 }
35
36 if !state.is_inside_code_block {
37 let heading = try_parse_heading(&line);
38
39 if let Some(heading) = heading {
40 if heading.depth <= state.depth {
41 state.exit_matched_section();
42 }
43
44 if !state.is_within_matched_section && regex.is_match(&heading.content) {
45 state.enter_matched_section(&heading);
46 }
47 }
48 }
49
50 if state.is_within_matched_section {
51 state.current.as_mut().unwrap().push(line);
52 }
53 }
54
55 state.push_current();
56
57 return state.matches;
58}