gcg_parser/
gcg.rs

1pub mod events;
2mod player;
3pub mod token;
4
5use crate::error::{Error, Result};
6pub use player::Player;
7
8#[derive(Debug, PartialEq)]
9pub struct Gcg {
10	pub player1: Player,
11	pub player2: Player,
12	/// A long description of the game. May contain HTML entities
13	pub description: Option<String>,
14}
15
16impl Gcg {
17	pub fn build(text: &str) -> Result<Gcg> {
18		let mut player1 = None::<Player>;
19		let mut player2 = None::<Player>;
20		let mut description = None::<String>;
21
22		for (i, line) in text.lines().enumerate() {
23			if line.starts_with("#player1") {
24				let player = Player::build(line).map_err(|e| Error::InvalidLine {
25					line_index: i.saturating_add(1),
26					source: e,
27				})?;
28				player1 = Some(player);
29			} else if line.starts_with("#player2") {
30				let player = Player::build(line).map_err(|e| Error::InvalidLine {
31					line_index: i.saturating_add(1),
32					source: e,
33				})?;
34				player2 = Some(player);
35			} else if line.starts_with("#description") {
36				let (_, desc) = line.split_once(' ').unwrap_or_default();
37
38				description = Some(desc.to_string());
39			} else {
40				return Err(Error::UnknownPragma {
41					line: text.to_string(),
42					line_index: i.saturating_add(1),
43				});
44			}
45		}
46
47		let gcg = Gcg {
48			player1: player1.ok_or_else(|| Error::MissingPragma {
49				keyword: "player1".to_string(),
50			})?,
51			player2: player2.ok_or_else(|| Error::MissingPragma {
52				keyword: "player2".to_string(),
53			})?,
54			description,
55		};
56
57		Ok(gcg)
58	}
59}
60
61#[cfg(test)]
62mod tests {
63	use super::*;
64	use anyhow::{Ok, Result};
65
66	#[test]
67	fn should_parse_player_names() -> Result<()> {
68		let text = [
69			"#player1 20jasper Jacob Asper",
70			"#player2 xXFerrisXx Ferris The Crab",
71		]
72		.join("\n");
73
74		let gcg = Gcg::build(&text)?;
75
76		assert_eq!(
77			gcg.player1,
78			Player {
79				nickname: "20jasper".to_string(),
80				full_name: "Jacob Asper".to_string(),
81			},
82		);
83		assert_eq!(
84			gcg.player2,
85			Player {
86				nickname: "xXFerrisXx".to_string(),
87				full_name: "Ferris The Crab".to_string(),
88			},
89		);
90
91		Ok(())
92	}
93
94	#[test]
95	fn should_error_when_missing_player() {
96		let text = ["#player2 20jasper Jacob Asper"].join("\n");
97
98		let error = Gcg::build(&text)
99			.unwrap_err()
100			.to_string()
101			.to_lowercase();
102
103		assert!(error.contains("player1"));
104	}
105
106	#[test]
107	fn should_error_with_unknown_pragma() {
108		let text = ["#whatisthispragma what idk"].join("\n");
109
110		let error = Gcg::build(&text)
111			.unwrap_err()
112			.to_string()
113			.to_lowercase();
114
115		assert!(error.contains("unknown pragma"));
116		assert!(error.contains("#whatisthispragma"));
117	}
118
119	#[test]
120	fn should_parse_description() -> Result<()> {
121		let text = [
122			"#player1 20jasper Jacob Asper",
123			"#player2 xXFerrisXx Ferris The Crab",
124			"#description 20jasper vs xXFerrisXx",
125		]
126		.join("\n");
127
128		let gcg = Gcg::build(&text)?;
129
130		assert_eq!(gcg.description, Some("20jasper vs xXFerrisXx".to_string()));
131
132		Ok(())
133	}
134}