1use std::collections::VecDeque;
2use pulldown_cmark::{Event, CowStr};
3use crate::{find_one_of, find_puncts_end};
4use crate::parser::split_speech_heading;
5
6#[derive(Debug,Clone,PartialEq)]
7pub struct Speech<'a> {
8 pub heading: Heading<'a>,
9 pub body: Vec<Inline<'a>>,
10}
11
12#[derive(Debug,Clone,PartialEq)]
13pub struct Heading<'a> {
14 pub character: CowStr<'a>,
15 pub direction: Direction<'a>,
16}
17
18#[derive(Debug,Clone,PartialEq)]
19pub enum Inline<'a> {
20 Event(Event<'a>),
21 Direction(Direction<'a>),
22}
23
24#[derive(Debug,Clone,PartialEq)]
25pub struct Direction<'a>(pub Vec<Event<'a>>);
26
27impl<'a> Direction<'a> {
28 pub fn new() -> Self {
29 Self(Vec::new())
30 }
31
32 pub fn push_string(&mut self, s: String) {
33 self.0.push(Event::Text(s.into()));
34 }
35}
36
37pub fn parse_speech<'a>(events: Vec<Event<'a>>) -> Result<Speech<'a>, Vec<Event<'a>>> {
38 let (heading, first) = match events.first() {
39 Some(Event::Text(s)) => {
40 let s = s.to_string();
41 if let Some((heading, line)) = split_speech_heading(s.as_ref()) {
42 let heading = heading.to_owned();
43 let line = line.to_owned();
44 (parse_heading(&heading), Event::Text(line.into()))
45 } else {
46 return Err(events);
47 }
48 },
49 _ => return Err(events),
50 };
51
52 let mut speech = vec![first];
53 for e in events.into_iter().skip(1) {
54 speech.push(e);
55 }
56 remove_trailing_softbreak(&mut speech);
57
58 let body = parse_body(speech);
59
60 Ok(Speech {
61 heading: heading,
62 body: body,
63 })
64}
65
66pub fn parse_heading(s: &str) -> Heading<'static> {
67 let open_paren = match s.find('(') {
68 Some(pos) => pos,
69 None => {
70 let character = s.trim().to_owned();
71 return Heading {
72 character: character.into(),
73 direction: Direction::new(),
74 };
75 },
76 };
77
78 let character = s[..open_paren].trim().to_owned();
79 let s = &s[open_paren+1..];
80 let mut close_paren = s.len();
81 for (index, c) in s.char_indices() {
82 if c == ')' {
83 close_paren = index;
84 break;
85 }
86 }
87
88 let s = s[..close_paren].to_owned();
89 let mut direction = Direction::new();
90 direction.push_string(s);
91
92 Heading {
93 character: character.into(),
94 direction: direction,
95 }
96}
97
98pub fn parse_body<'a>(events: Vec<Event<'a>>) -> Vec<Inline<'a>> {
99 let mut body = Vec::new();
100 let mut direction = Vec::new();
101 let mut paren_level = 0usize;
102
103 for event in ParenSplitter::new(events.into_iter()) {
104 match event {
105 Event::Text(s) if s.as_ref() == "(" => {
106 if paren_level > 0 {
107 direction.push(Event::Text(s));
108 }
109
110 paren_level = paren_level + 1;
111 },
112 Event::Text(s) if s.as_ref() == ")" => {
113 match paren_level {
114 0 => {
115 body.push(Inline::Event(Event::Text(s)));
116 },
117 1 => {
118 let mut pushed = Vec::new();
119 std::mem::swap(&mut pushed, &mut direction);
120 let pushed = Direction(pushed);
121 body.push(Inline::Direction(pushed));
122 paren_level = paren_level - 1;
123 },
124 _ => {
125 direction.push(Event::Text(s));
126 paren_level = paren_level -1;
127 },
128 }
129 },
130 _ => {
131 if paren_level > 0 {
132 direction.push(event);
133 } else {
134 body.push(Inline::Event(event));
135 }
136 },
137 }
138 }
139
140 if direction.len() > 0 {
141 let direction = Direction(direction);
142 body.push(Inline::Direction(direction));
143 }
144
145 trim_start_of_line_head(body)
146}
147
148#[derive(Debug)]
149pub struct ParenSplitter<'a, I> {
150 iter: I,
151 queue: VecDeque<Event<'a>>,
152}
153
154impl<'a, I> ParenSplitter<'a, I>
155where
156 I: Iterator<Item=Event<'a>>,
157{
158 pub fn new(iter: I) -> Self {
159 Self {
160 iter: iter,
161 queue: VecDeque::new(),
162 }
163 }
164}
165
166impl<'a, I> Iterator for ParenSplitter<'a, I>
167where
168 I: Iterator<Item=Event<'a>>,
169{
170 type Item = Event<'a>;
171
172 fn next(&mut self) -> Option<Self::Item> {
173 if let Some(event) = self.queue.pop_front() {
174 return Some(event);
175 }
176
177 match self.iter.next() {
178 Some(Event::Text(s)) => {
179 for text in split_at_paren(s).into_iter() {
180 self.queue.push_back(Event::Text(text.into()));
181 }
182 },
183 item => return item,
184 }
185
186 self.queue.pop_front()
187 }
188}
189
190fn split_at_paren<T: AsRef<str>>(s: T) -> Vec<String> {
191 let mut s = s.as_ref();
192 let mut v = Vec::new();
193
194 loop {
195 if s.len() == 0 {
196 break;
197 }
198
199 match find_one_of(s, "()") {
200 Some((index, c)) => {
201 let before = &s[..index];
202 let (parens, after) = find_puncts_end(&s[index..], c);
203 v.push(before.to_owned());
204 v.push(parens.to_owned());
205 s = after;
206 },
207 None => {
208 v.push(s.to_owned());
209 s = "";
210 },
211 }
212 }
213
214 v
215}
216
217pub fn trim_start_of_line_head<'a>(body: Vec<Inline<'a>>) -> Vec<Inline<'a>> {
218 let mut ret = Vec::with_capacity(body.len());
219 let mut is_line_head = true;
220
221 for inline in body.into_iter() {
222 match (inline, is_line_head) {
223 (Inline::Event(Event::Text(s)), true) => {
224 let trimmed = s.trim_start();
225 if trimmed.len() > 0 {
226 let trimmed = trimmed.to_owned();
227 ret.push(Inline::Event(Event::Text(trimmed.into())));
228 }
229 is_line_head = false;
230 },
231 (inline @ Inline::Event(Event::SoftBreak), _) => {
232 ret.push(inline);
233 is_line_head = true;
234 },
235 (inline, _) => {
236 ret.push(inline);
237 is_line_head = false;
238 },
239 }
240 }
241
242 ret
243}
244
245fn remove_trailing_softbreak<'a>(events: &mut Vec<Event<'a>>) {
246 match events.pop() {
247 Some(Event::SoftBreak) => {},
248 Some(e) => {
249 events.push(e);
250 },
251 None => {},
252 }
253}
254
255#[cfg(test)]
256mod test {
257 use super::*;
258 use pulldown_cmark::Event;
259 use big_s::S;
260
261 #[test]
262 fn parse_heading_only_with_character() {
263 assert_eq!(parse_heading("A "), Heading {
264 character: "A".into(),
265 direction: Direction::new(),
266 });
267 }
268
269 #[test]
270 fn parse_heading_with_direction() {
271 assert_eq!(parse_heading("A (running) "), Heading {
272 character: "A".into(),
273 direction: Direction(vec![Event::Text("running".into())]),
274 });
275 }
276
277 #[test]
278 fn split_parens_in_direction() {
279 assert_eq!(split_at_paren("A (running)"), vec![S("A "), S("("), S("running"), S(")")]);
280 assert_eq!(split_at_paren("xx (dd) yy"), vec![S("xx "), S("("), S("dd"), S(")"), S(" yy")]);
281 assert_eq!(split_at_paren("Escaped (( example"), vec![S("Escaped "), S("(("), S(" example")]);
282 }
283
284 #[test]
285 fn paren_splitter_for_two_lines() {
286 let v = vec![Event::Text("Hello! (xxx)".into()), Event::SoftBreak, Event::Text("Bye!".into())];
287 let mut iter = ParenSplitter::new(v.into_iter());
288
289 assert_eq!(iter.next(), Some(Event::Text("Hello! ".into())));
290 assert_eq!(iter.next(), Some(Event::Text("(".into())));
291 assert_eq!(iter.next(), Some(Event::Text("xxx".into())));
292 assert_eq!(iter.next(), Some(Event::Text(")".into())));
293 assert_eq!(iter.next(), Some(Event::SoftBreak));
294 assert_eq!(iter.next(), Some(Event::Text("Bye!".into())));
295 assert_eq!(iter.next(), None);
296 }
297
298 #[test]
299 fn parse_body_only_with_text() {
300 let v = vec![Event::Text("Hello!".into()), Event::SoftBreak];
301 assert_eq!(parse_body(v), vec![Inline::Event(Event::Text("Hello!".into())), Inline::Event(Event::SoftBreak)]);
302 }
303
304 #[test]
305 fn parse_body_with_direction() {
306 let input = vec![
307 Event::Text("Hello! (running) Bye!".into()),
308 ];
309 let output = vec![
310 Inline::Event(Event::Text("Hello! ".into())),
311 Inline::Direction(Direction(
312 vec![Event::Text("running".into())]
313 )),
314 Inline::Event(Event::Text(" Bye!".into())),
315 ];
316 assert_eq!(parse_body(input), output);
317 }
318
319 #[test]
320 fn parse_body_with_nested_parens() {
321 let input = vec![
322 Event::Text("Hello! (running (xxx) ) Bye!".into()),
323 ];
324 let output = vec![
325 Inline::Event(Event::Text("Hello! ".into())),
326 Inline::Direction(Direction(vec![
327 Event::Text("running ".into()),
328 Event::Text("(".into()),
329 Event::Text("xxx".into()),
330 Event::Text(")".into()),
331 Event::Text(" ".into()),
332 ])),
333 Inline::Event(Event::Text(" Bye!".into())),
334 ];
335 assert_eq!(parse_body(input), output);
336 }
337
338 #[test]
339 fn parse_speech_of_one_line() {
340 let input = vec![
341 Event::Text("A (running)> Hello! (exit)".into()),
342 ];
343 let output = Speech {
344 heading: Heading {
345 character: "A".into(),
346 direction: Direction(vec![Event::Text("running".into())]),
347 },
348 body: vec![
349 Inline::Event(Event::Text("Hello! ".into())),
350 Inline::Direction(Direction(vec![
351 Event::Text("exit".into()),
352 ])),
353 ],
354 };
355 assert_eq!(parse_speech(input), Ok(output));
356 }
357
358 #[test]
359 fn trim_start_of_body_line_head() {
360 let input = vec![
361 Inline::Event(Event::Text(" Hello!".into())),
362 Inline::Event(Event::SoftBreak),
363 Inline::Event(Event::Text(" Ah!".into())),
364 Inline::Event(Event::SoftBreak),
365 Inline::Event(Event::Text(" Oh!".into())),
366 Inline::Direction(Direction(vec![Event::Text("exit".into())])),
367 Inline::Event(Event::Text(" zzz".into())),
368 ];
369 let output = vec![
370 Inline::Event(Event::Text("Hello!".into())),
371 Inline::Event(Event::SoftBreak),
372 Inline::Event(Event::Text("Ah!".into())),
373 Inline::Event(Event::SoftBreak),
374 Inline::Event(Event::Text("Oh!".into())),
375 Inline::Direction(Direction(vec![Event::Text("exit".into())])),
376 Inline::Event(Event::Text(" zzz".into())),
377 ];
378 assert_eq!(trim_start_of_line_head(input), output);
379 }
380}