org_rust_parser/element/
drawer.rs1use std::borrow::Cow;
2use std::collections::HashMap;
3
4use crate::constants::{COLON, HYPHEN, NEWLINE, UNDERSCORE};
5use crate::node_pool::NodeID;
6use crate::object::parse_node_property;
7use crate::parse::parse_element;
8use crate::types::{Cursor, MatchError, ParseOpts, Parseable, Parser, Result};
9use crate::utils::Match;
10
11use lazy_static::lazy_static;
12use regex::bytes::Regex;
13
14lazy_static! {
16 static ref END_RE: Regex = Regex::new(r"(?mi)^[ \t]*:end:[\t ]*$").unwrap();
17}
18
19#[derive(Debug, Clone)]
20pub struct Drawer<'a> {
21 pub children: Vec<NodeID>,
22 pub name: &'a str,
23}
24
25impl<'a> Parseable<'a> for Drawer<'a> {
26 fn parse(
27 parser: &mut Parser<'a>,
28 mut cursor: Cursor<'a>,
29 parent: Option<NodeID>,
30 parse_opts: ParseOpts,
31 ) -> Result<NodeID> {
32 let start = cursor.index;
33 cursor.skip_ws();
34 cursor.word(":")?;
35
36 let name_match = cursor.fn_until(|chr| {
37 chr == COLON
38 || chr == NEWLINE
39 || !(chr.is_ascii_alphanumeric() || chr == HYPHEN || chr == UNDERSCORE)
40 })?;
41
42 cursor.index = name_match.end;
43 cursor.word(":")?;
44 cursor.skip_ws();
45 if cursor.try_curr()? != NEWLINE {
46 return Err(MatchError::InvalidLogic);
47 }
48 cursor.next();
49 let matched_reg = END_RE.find(cursor.rest()).ok_or(MatchError::InvalidLogic)?;
50 let loc = matched_reg.start() + cursor.index;
51 let end = matched_reg.end() + cursor.index;
52
53 let mut children: Vec<NodeID> = Vec::new();
57 let reserve_id = parser.pool.reserve_id();
58 let mut temp_cursor = cursor.cut_off(loc);
59
60 while let Ok(element_id) =
63 parse_element(parser, temp_cursor, Some(reserve_id), ParseOpts::default())
66 {
67 children.push(element_id);
68 temp_cursor.index = parser.pool[element_id].end;
69 }
70
71 Ok(parser.alloc_with_id(
72 Self {
73 children,
74 name: name_match.obj,
75 },
76 start,
77 end,
78 parent,
79 reserve_id,
80 ))
81 }
82}
83
84pub type PropertyDrawer<'a> = HashMap<&'a str, Cow<'a, str>>;
85
86pub(crate) fn parse_property(mut cursor: Cursor) -> Result<Match<PropertyDrawer>> {
87 cursor.curr_valid()?;
88 let start = cursor.index;
89 cursor.skip_ws();
90 cursor.word(":")?;
91
92 let name_match = cursor.fn_until(|chr| chr == COLON || chr == NEWLINE)?;
93
94 if name_match.obj.to_ascii_lowercase() != "properties" {
95 return Err(MatchError::InvalidLogic);
96 }
97 cursor.index = name_match.end;
98
99 cursor.word(":")?;
100 cursor.skip_ws();
101 if cursor.try_curr()? != NEWLINE {
102 return Err(MatchError::InvalidLogic);
103 }
104 cursor.next();
105 let matched_reg = END_RE.find(cursor.rest()).ok_or(MatchError::InvalidLogic)?;
106 let loc = matched_reg.start() + cursor.index;
107 let end = matched_reg.end() + cursor.index;
108
109 let mut children = HashMap::new();
113 let mut temp_cursor = cursor.cut_off(loc);
114 loop {
115 match parse_node_property(temp_cursor, &mut children) {
116 Ok(node_end) => {
117 temp_cursor.index = node_end;
118 }
119 Err(MatchError::EofError) => break,
120 Err(e) => return Err(e),
121 }
122 }
123
124 Ok(Match {
125 start,
126 end,
127 obj: children,
128 })
129}
130
131#[cfg(test)]
132mod tests {
133 use crate::parse_org;
134
135 #[test]
136 fn basic_drawer() {
137 let input = r"
138
139:NAME:
140hello
141:end:
142
143halloo
144";
145
146 let pool = parse_org(input);
147 pool.print_tree();
148 }
149
150 #[test]
151 fn basic_drawer_caps() {
152 let input = r"
153
154:NAME:
155hello
156:END:
157
158halloo
159";
160
161 let pool = parse_org(input);
162 pool.print_tree();
163 }
164}