s_text_input_f_parser/
paragraph.rs1use chumsky::prelude::*;
2use s_text_input_f::{Paragraph, ParagraphItem};
3
4#[derive(Debug)]
5pub(crate) struct CorrectParagraphItem {
6 pub(crate) input: ParagraphItem,
7 pub(crate) answer: Option<String>,
8}
9
10#[derive(Debug)]
11pub struct CorrectParagraph {
12 pub input: Paragraph,
13 pub answer: Vec<String>,
14}
15
16impl FromIterator<CorrectParagraphItem> for CorrectParagraph {
17 fn from_iter<T: IntoIterator<Item = CorrectParagraphItem>>(iter: T) -> Self {
18 let (input, answers): (Vec<_>, Vec<_>) = iter
19 .into_iter()
20 .map(|CorrectParagraphItem { input, answer }| (input, answer))
21 .unzip();
22 let answer = answers.into_iter().flatten().collect::<Vec<_>>();
23 Self { input, answer }
24 }
25}
26
27pub(crate) fn paragraph_parser() -> impl Parser<char, CorrectParagraph, Error = Simple<char>> {
28 let text = filter::<_, _, Simple<char>>(|&ch| {
29 ch != '`'
30 && ch != '\n'
31 && (char::is_alphanumeric(ch)
32 || char::is_whitespace(ch)
33 || char::is_ascii_punctuation(&ch))
34 })
35 .repeated()
36 .at_least(1)
37 .collect::<String>();
38 let input_field = just('`')
39 .ignore_then(
40 filter(|&ch| {
41 ch != '`'
42 && (char::is_alphanumeric(ch)
43 || char::is_whitespace(ch)
44 || char::is_ascii_punctuation(&ch))
45 })
46 .repeated()
47 .collect::<String>(),
48 )
49 .then_ignore(just('`'));
50
51 let paragraph_item = choice((
52 text.map(|text| CorrectParagraphItem {
53 input: ParagraphItem::Text(text),
54 answer: None,
55 }),
56 input_field.map(|text| CorrectParagraphItem {
57 input: ParagraphItem::Placeholder,
58 answer: Some(text),
59 }),
60 ));
61
62 paragraph_item
63 .repeated()
64 .at_least(1)
65 .map(CorrectParagraph::from_iter)
66 .then_ignore(choice((just('\n').ignored(), end().ignored())))
67}