simploxide_bindgen/
events.rs

1//! Turns EVENTS.md file into na Iterator that yields [`types::DisjointedDisriminatedUnionVariant`].
2
3use crate::{parse_utils, types::DisjointedDisriminatedUnionVariant};
4
5pub fn parse(
6    events_md: &str,
7) -> impl Iterator<Item = Result<DisjointedDisriminatedUnionVariant, String>> {
8    let mut parser = Parser::default();
9
10    events_md
11        .split("---")
12        .skip(1)
13        .filter_map(|s| {
14            let trimmed = s.trim();
15            (!trimmed.is_empty()).then_some(trimmed)
16        })
17        .map(move |blk| parser.parse_block(blk))
18}
19
20#[derive(Default)]
21struct Parser {
22    current_doc_section: Option<DocSection>,
23}
24
25impl Parser {
26    pub fn parse_block(
27        &mut self,
28        block: &str,
29    ) -> Result<DisjointedDisriminatedUnionVariant, String> {
30        self.parser(block.lines().map(str::trim))
31            .map_err(|e| format!("{e} in block\n```\n{block}\n```"))
32    }
33
34    fn parser<'a>(
35        &mut self,
36        mut lines: impl Iterator<Item = &'a str>,
37    ) -> Result<DisjointedDisriminatedUnionVariant, String> {
38        const DOC_SECTION_PAT: &str = parse_utils::H2;
39        const TYPENAME_PAT: &str = parse_utils::H3;
40        const TYPEKIND_PAT: &str = parse_utils::BOLD;
41
42        let mut next =
43            parse_utils::skip_empty(&mut lines).ok_or_else(|| "Got an empty block".to_owned())?;
44
45        let mut inner_docs: Vec<String> = Vec::new();
46
47        loop {
48            if let Some(section_name) = next.strip_prefix(DOC_SECTION_PAT) {
49                let mut doc_section = DocSection::new(section_name.to_owned());
50
51                next = parse_utils::parse_doc_lines(&mut lines, &mut doc_section.contents, |s| {
52                    s.starts_with(TYPENAME_PAT)
53                })
54                .ok_or_else(|| format!("Failed to find a typename by pattern {TYPENAME_PAT:?} after the doc section"))?;
55
56                self.current_doc_section.replace(doc_section);
57            } else if let Some(_name) = next.strip_prefix(TYPENAME_PAT) {
58                parse_utils::parse_doc_lines(&mut lines, &mut inner_docs, |s| {
59                    s.starts_with(TYPEKIND_PAT)
60                }).ok_or_else(|| format!("Failed to find a typekind by pattern {TYPEKIND_PAT:?} after the inner docs "))?;
61
62                break;
63            }
64        }
65
66        let (variant, _) = parse_utils::parse_discriminated_union_variant(&mut lines)?;
67        let mut disjointed = variant.disjoin();
68
69        // Spread documentation between enum and structs
70        //
71        if let Some(ref outer_docs) = self.current_doc_section {
72            disjointed
73                .variant
74                .doc_comments
75                .push(outer_docs.header.clone());
76
77            disjointed
78                .record
79                .doc_comments
80                .push(format!("### {}", outer_docs.header.clone()));
81
82            disjointed.record.doc_comments.push(String::new());
83
84            disjointed
85                .record
86                .doc_comments
87                .extend(outer_docs.contents.iter().cloned());
88
89            disjointed.record.doc_comments.push(String::new());
90            disjointed.record.doc_comments.push("----".to_owned());
91            disjointed.record.doc_comments.push(String::new());
92        }
93
94        disjointed.record.doc_comments.extend(inner_docs);
95        Ok(disjointed)
96    }
97}
98
99#[derive(Default, Clone)]
100struct DocSection {
101    header: String,
102    contents: Vec<String>,
103}
104
105impl DocSection {
106    fn new(header: String) -> Self {
107        Self {
108            header,
109            contents: Vec::new(),
110        }
111    }
112}