dialogue_rs/script/
line.rs

1//! # Lines
2//! 
3//! Lines in a script are either [Markers](#Markers) or [Commands](#Commands). They can include any letters
4//! or symbols, except for pipes _( | )_, as those are used to delimit commands.
5
6use crate::script::{
7    command::Command,
8    marker::Marker,
9    parser::{Parser, Rule},
10};
11use anyhow::bail;
12use pest::{iterators::Pair, Parser as PestParser};
13use std::fmt;
14
15/// A line in a script.
16#[derive(Debug, PartialEq, Eq, Clone)]
17pub enum Line {
18    /// A [Command].
19    Command(Command),
20    /// A [Marker].
21    Marker(Marker),
22}
23
24impl fmt::Display for Line {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            Self::Command(command) => writeln!(f, "{command}"),
28            Self::Marker(marker) => writeln!(f, "{marker}"),
29        }
30    }
31}
32
33impl From<Command> for Line {
34    fn from(command: Command) -> Self {
35        Self::Command(command)
36    }
37}
38
39impl From<Marker> for Line {
40    fn from(marker: Marker) -> Self {
41        Self::Marker(marker)
42    }
43}
44
45impl Line {
46    /// Create a new [Line] from a [Command].
47    pub fn command(command: Command) -> Self {
48        Self::Command(command)
49    }
50
51    /// Create a new [Line] from a string.
52    pub fn parse(line_str: &str) -> Result<Self, anyhow::Error> {
53        let mut pairs = Parser::parse(Rule::Line, line_str)?;
54        let pair = pairs.next().expect("a pair exists");
55        assert_eq!(pairs.next(), None);
56
57        pair.try_into()
58    }
59}
60
61impl TryFrom<Pair<'_, Rule>> for Line {
62    type Error = anyhow::Error;
63
64    fn try_from(pair: Pair<'_, Rule>) -> Result<Self, Self::Error> {
65        match pair.as_rule() {
66            Rule::Line => {
67                let mut pairs = pair.into_inner();
68                let pair = pairs.next().expect("a pair exists");
69                assert_eq!(pairs.next(), None);
70
71                match pair.as_rule() {
72                    Rule::Command => Command::parse(pair.as_str()).map(Self::Command),
73                    Rule::Marker => Marker::parse(pair.as_str()).map(Self::Marker),
74                    _ => unreachable!("Lines can't contain anything other than commands, comments, markers, or blank lines"),
75                }
76            }
77            _ => bail!("Pair is not a line: {:#?}", pair),
78        }
79    }
80}
81
82#[cfg(test)]
83mod test {
84    use crate::script::command::Command;
85
86    use super::Line;
87
88    #[test]
89    fn test_parse_line() {
90        let expected = Line::Command(Command::new(
91            "SAY",
92            Some("Zelda"),
93            Some("\"I don't actually have anything to say\""),
94        ));
95        let actual = Line::parse("Zelda |SAY| \"I don't actually have anything to say\"\n\n")
96            .expect("command is valid");
97
98        assert_eq!(expected, actual);
99    }
100
101    #[test]
102    fn test_round_trip() {
103        let input = "|SAY| Does this work?\n";
104        let line = Line::parse(input).expect("line is valid");
105        let output = line.to_string();
106
107        assert_eq!(input, output);
108    }
109}