1pub mod block;
4pub mod command;
5pub mod comment;
6pub mod element;
7pub mod line;
8pub mod marker;
9pub(crate) mod parser;
10
11use self::{block::Block, element::TopLevelElement, line::Line};
12use anyhow::bail;
13use parser::{Parser, Rule};
14use pest::{iterators::Pair, Parser as PestParser};
15use std::fmt;
16
17#[derive(Debug, Default)]
19pub struct Script(pub Vec<TopLevelElement>);
20
21impl fmt::Display for Script {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 for el in &self.0 {
24 write!(f, "{el}")?;
25 }
26
27 Ok(())
28 }
29}
30
31impl Script {
32 pub fn empty() -> Self {
34 Default::default()
35 }
36
37 pub fn parse(script_str: &str) -> Result<Self, anyhow::Error> {
39 let mut pairs = Parser::parse(Rule::Script, script_str)?;
40 let pair = pairs.next().expect("a pair exists");
41 assert_eq!(pairs.next(), None);
42
43 pair.try_into()
44 }
45}
46
47impl TryFrom<Pair<'_, Rule>> for Script {
48 type Error = anyhow::Error;
49
50 fn try_from(pair: Pair<'_, Rule>) -> Result<Self, Self::Error> {
51 match pair.as_rule() {
52 Rule::Script => {
53 let inner = pair
54 .into_inner()
55 .map(|pair| match pair.as_rule() {
56 Rule::Block => Block::parse(pair.as_str()).map(Into::into),
57 Rule::Line => Line::parse(pair.as_str()).map(Into::into),
58 _ => unreachable!(
59 "Scripts can't contain anything other than blocks or lines but found {:?}",
60 pair.as_rule()
61 ),
62 })
63 .collect::<Result<Vec<_>, _>>()?;
64
65 Ok(Self(inner))
66 }
67 _ => bail!("Pair is not a script: {:#?}", pair),
68 }
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::Script;
75 use pretty_assertions::assert_eq;
76
77 #[test]
78 fn test_complex_is_nesting_parsed_correctly() {
79 let input = "%START%
80|TEST| A
81 |TEST| B
82 |TEST| C
83 |TEST| D
84 |TEST| E
85 |TEST| F
86|TEST| G
87%END%
88";
89 let script = Script::parse(input).expect("a script can be parsed");
90 let actual = script.to_string();
91
92 assert_eq!(input, actual);
93 }
94
95 #[test]
96 fn test_script_to_string_matches_input_script_1() {
97 let input = std::fs::read_to_string("example_scripts/daisy-and-luigi.script")
98 .expect("example script exists");
99
100 let script = Script::parse(&input).expect("a script can be parsed");
101 assert_eq!(input, script.to_string());
103 }
104
105 #[test]
106 fn test_script_to_string_matches_input_script_2() {
107 let input = std::fs::read_to_string("example_scripts/capital-of-spain.script")
108 .expect("example script exists");
109
110 let script = Script::parse(&input).expect("a script can be parsed");
111 assert_eq!(input, script.to_string());
113 }
114
115 #[test]
116 fn test_script_to_string_matches_input_script_3() {
117 let input =
118 std::fs::read_to_string("example_scripts/jimi.script").expect("example script exists");
119
120 let script = Script::parse(&input).expect("a script can be parsed");
121 assert_eq!(input, script.to_string());
123 }
124}