1use std::collections::VecDeque;
2use pulldown_cmark::{Event, Tag};
3use crate::parser::{FuseOnParagraphEnd, Speeches};
4use crate::speech::{parse_speech, parse_body};
5use crate::renderer::HtmlRenderer;
6
7#[derive(Debug)]
8enum Mode {
9 Nop,
10 PlayScript,
11 Monologue,
12}
13
14impl Mode {
15 fn is_off(&self) -> bool {
16 match self {
17 Mode::Nop => true,
18 _ => false,
19 }
20 }
21
22 fn is_monologue(&self) -> bool {
23 match self {
24 Mode::Monologue => true,
25 _ => false,
26 }
27 }
28}
29
30#[derive(Debug,Clone)]
31pub struct Options {
32 replace_softbreaks_with: Option<String>,
33 disabled_in_default: bool,
34}
35
36impl Default for Options {
37 fn default() -> Self {
38 Self {
39 replace_softbreaks_with: Some(" ".to_owned()),
40 disabled_in_default: false,
41 }
42 }
43}
44
45impl Options {
46 pub fn default_ja() -> Self {
47 Self {
48 replace_softbreaks_with: Some("".to_owned()),
49 disabled_in_default: false,
50 }
51 }
52}
53
54#[derive(Debug,Default,Clone)]
55pub struct Params {
56 pub title: Option<String>,
57 pub subtitle: Option<String>,
58 pub authors: Vec<String>,
59}
60
61pub struct MdPlayScriptBuilder {
62 options: Option<Options>,
63 params: Option<Params>,
64 make_title: Option<Box<dyn FnMut(&Params) -> String>>,
65}
66
67impl MdPlayScriptBuilder {
68 pub fn new() -> Self {
69 Self {
70 options: None,
71 params: None,
72 make_title: None,
73 }
74 }
75
76 pub fn options(self, opt: Options) -> Self {
77 Self {
78 options: Some(opt),
79 ..self
80 }
81 }
82
83 pub fn params(self, p: Params) -> Self {
84 Self {
85 params: Some(p),
86 ..self
87 }
88 }
89
90 pub fn make_title(self, val: Box<dyn FnMut(&Params) -> String>) -> Self {
91 Self {
92 make_title: Some(val),
93 ..self
94 }
95 }
96
97 pub fn build<'a, I>(self, iter: I) -> MdPlayScript<'a, I>
98 where
99 I: Iterator<Item=Event<'a>>,
100 {
101 let options = self.options.unwrap();
102 let renderer = HtmlRenderer {
103 replace_softbreak: options.replace_softbreaks_with,
104 ..Default::default()
105 };
106 let mode = if options.disabled_in_default {
107 Mode::Nop
108 } else {
109 Mode::PlayScript
110 };
111
112 MdPlayScript {
113 iter: Some(iter),
114 queue: VecDeque::new(),
115 mode: mode,
116 params: self.params.unwrap_or(Params::default()),
117 renderer: renderer,
118 make_title: self.make_title,
119 }
120 }
121}
122
123pub struct MdPlayScript<'a, I> {
124 iter: Option<I>,
125 queue: VecDeque<Event<'a>>,
126 mode: Mode,
127 params: Params,
128 renderer: HtmlRenderer,
129 make_title: Option<Box<dyn FnMut(&Params) -> String>>,
130}
131
132impl<'a, I> MdPlayScript<'a, I>
133where
134 I: Iterator<Item=Event<'a>>,
135{
136 pub fn new(iter: I) -> Self {
137 Self {
138 iter: Some(iter),
139 queue: VecDeque::new(),
140 mode: Mode::PlayScript,
141 params: Default::default(),
142 renderer: Default::default(),
143 make_title: None,
144 }
145 }
146
147 pub fn renderer_mut(&mut self) -> &mut HtmlRenderer {
148 &mut self.renderer
149 }
150
151 pub fn into_inner(self) -> I {
152 self.iter.unwrap()
153 }
154
155 fn dispatch_directive(&mut self, s: &str) {
156 match parse_directive(&s) {
157 Some(Directive::MonologueBegin) => {
158 self.mode = Mode::Monologue;
159 },
160 Some(Directive::MonologueEnd) => {
161 self.mode = Mode::PlayScript;
162 },
163 Some(Directive::PlayScriptOn) => {
164 self.mode = Mode::PlayScript;
165 },
166 Some(Directive::PlayScriptOff) => {
167 self.mode = Mode::Nop;
168 },
169 Some(Directive::Title) => {
170 emit_title(&self.params, &mut self.queue);
171 },
172 Some(Directive::SubTitle) => {
173 emit_subtitle(&self.params, &mut self.queue);
174 },
175 Some(Directive::Authors) => {
176 emit_authors(&self.params, &mut self.queue);
177 },
178 Some(Directive::MakeTitle) => {
179 if let Some(make_title) = self.make_title.as_mut() {
180 let cover = (make_title)(&self.params);
181 self.queue.push_back(Event::Html(cover.into()));
182 }
183 },
184 None => {},
185 }
186 }
187
188 fn append_events(&mut self, events: Vec<Event<'a>>) {
189 self.queue.extend(events);
190 }
191
192 fn dispatch_speech(&mut self, speech: Vec<Event<'a>>) {
193 match parse_speech(speech) {
194 Ok(speech) => {
195 let mut html = Vec::new();
196 self.renderer.render_speech(speech, &mut html);
197 html.push(Event::SoftBreak);
198 self.append_events(html);
199 },
200 Err(para) if self.mode.is_monologue() => {
201 let monologue = parse_body(para);
202 let mut html = Vec::new();
203 self.renderer.render_monologue(monologue, &mut html);
204 self.append_events(html);
205 },
206 Err(para) => {
207 let mut output = Vec::new();
208 self.renderer.render_events(para, &mut output);
209 self.append_events(wrap_by_paragraph_tag(output));
210 },
211 };
212 }
213}
214
215impl<'a, I: 'a> Iterator for MdPlayScript<'a, I>
216where
217 I: Iterator<Item=Event<'a>>,
218{
219 type Item = Event<'a>;
220
221 fn next(&mut self) -> Option<Self::Item> {
222 if let Some(event) = self.queue.pop_front() {
223 return Some(event);
224 }
225
226 let mut iter = self.iter.take().unwrap();
227
228 match iter.next() {
229 Some(Event::Html(s)) => {
230 self.dispatch_directive(&s);
231 self.queue.push_back(Event::Html(s));
232 },
233 Some(Event::Start(Tag::Paragraph)) if !self.mode.is_off() => {
234 let mut speeches = Speeches::new(FuseOnParagraphEnd::new(iter));
235
236 while let Some(speech) = speeches.next() {
237 self.dispatch_speech(speech);
238 }
239
240 iter = speeches.into_inner().into_inner();
241 },
242 Some(event) => {
243 self.queue.push_back(event);
244 },
245 None => {},
246 }
247
248 self.iter.replace(iter);
249
250 self.queue.pop_front()
251 }
252}
253
254fn wrap_events_by<'a, 'b: 'a>(mut events: Vec<Event<'a>>, start: Event<'b>, end: Event<'b>) -> Vec<Event<'a>> {
255 let mut output = Vec::with_capacity(events.len() + 2);
256
257 output.push(start);
258 output.append(&mut events);
259 output.push(end);
260
261 output
262}
263
264fn wrap_by_paragraph_tag<'a>(events: Vec<Event<'a>>) -> Vec<Event<'a>> {
265 wrap_events_by(
266 events,
267 Event::Start(Tag::Paragraph),
268 Event::End(Tag::Paragraph),
269 )
270}
271
272#[derive(Debug,Clone,PartialEq)]
273enum Directive {
274 MonologueBegin,
275 MonologueEnd,
276 PlayScriptOn,
277 PlayScriptOff,
278 Title,
279 SubTitle,
280 Authors,
281 MakeTitle,
282}
283
284fn parse_directive(s: &str) -> Option<Directive> {
285 let s = s.trim()
286 .strip_prefix("<!--")?
287 .strip_suffix("-->")?
288 .trim();
289
290 match s {
291 "playscript-monologue-begin" => Some(Directive::MonologueBegin),
292 "playscript-monologue-end" => Some(Directive::MonologueEnd),
293 "playscript-on" => Some(Directive::PlayScriptOn),
294 "playscript-off" => Some(Directive::PlayScriptOff),
295 "playscript-title" => Some(Directive::Title),
296 "playscript-subtitle" => Some(Directive::SubTitle),
297 "playscript-authors" => Some(Directive::Authors),
298 "playscript-make-title" => Some(Directive::MakeTitle),
299 _ => None,
300 }
301}
302
303fn emit_title<'a>(params: &Params, queue: &mut VecDeque<Event<'a>>) {
304 let p_start = "<h1 class=\"cover-title\">";
305 let p_end = "</h1>";
306
307 if let Some(content) = params.title.as_ref().cloned() {
308 queue.push_back(Event::Html(p_start.into()));
309 queue.push_back(Event::Text(content.into()));
310 queue.push_back(Event::Html(p_end.into()));
311 }
312}
313
314fn emit_subtitle<'a>(params: &Params, queue: &mut VecDeque<Event<'a>>) {
315 let p_start = "<h2 class=\"cover-title\">";
316 let p_end = "</h2>";
317
318 if let Some(content) = params.subtitle.as_ref().cloned() {
319 queue.push_back(Event::Html(p_start.into()));
320 queue.push_back(Event::Text(content.into()));
321 queue.push_back(Event::Html(p_end.into()));
322 }
323}
324
325fn emit_authors<'a>(params: &Params, queue: &mut VecDeque<Event<'a>>) {
326 let div_start = "<div class=\"authors\">";
327 let div_end = "</div>";
328 let p_start = "<p class=\"cover-author\">";
329 let p_end = "</p>";
330
331 if params.authors.is_empty() {
332 return;
333 }
334
335 queue.push_back(Event::Html(div_start.into()));
336
337 for author in params.authors.iter().cloned() {
338 queue.push_back(Event::Html(p_start.into()));
339 queue.push_back(Event::Text(author.into()));
340 queue.push_back(Event::Html(p_end.into()));
341 }
342
343 queue.push_back(Event::Html(div_end.into()));
344}
345
346#[cfg(test)]
347mod test {
348 use super::*;
349 use pulldown_cmark::Parser;
350 use pulldown_cmark::html::push_html;
351
352 #[test]
353 fn consume() {
354 let s = r#"A> xxx
355\ruby
356B> Hello
357
358This is a normal line.
359
360<!-- playscript-monologue-begin -->
361Monologue
362<!-- playscript-monologue-end -->
363
364A> What?
365????
366B> !!!!
367A> ...."#;
368 let parser = MdPlayScript::new(Parser::new(s));
369
370 for e in parser {
371 eprintln!("{:?}", e);
372 }
373
374 let mut buf = String::new();
375 let parser = MdPlayScript::new(Parser::new(s));
376
377 push_html(&mut buf, parser);
378
379 eprintln!("{}", buf);
380 }
381
382 #[test]
383 fn parse_correct_directives() {
384 assert_eq!(
385 parse_directive("<!-- playscript-monologue-begin -->"),
386 Some(Directive::MonologueBegin));
387 assert_eq!(
388 parse_directive("<!-- playscript-monologue-end -->"),
389 Some(Directive::MonologueEnd));
390 assert_eq!(
391 parse_directive("<!-- playscript-on -->"),
392 Some(Directive::PlayScriptOn));
393 assert_eq!(
394 parse_directive("<!-- playscript-off -->"),
395 Some(Directive::PlayScriptOff));
396 assert_eq!(
397 parse_directive("<!-- playscript-title -->"),
398 Some(Directive::Title));
399 assert_eq!(
400 parse_directive("<!-- playscript-subtitle -->"),
401 Some(Directive::SubTitle));
402 assert_eq!(
403 parse_directive("<!-- playscript-authors -->"),
404 Some(Directive::Authors));
405 }
406}