1use specmc_base::{
2 ensure_tokens,
3 parse::{Identifier, Parse, ParseError},
4};
5use strtoint::strtoint;
6
7use crate::base::FieldList;
8
9#[derive(Debug, Clone, PartialEq, Eq, Hash)]
10pub enum Direction {
11 Serverbound,
12 Clientbound,
13}
14impl Parse for Direction {
15 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
16 use Direction::*;
17 match tokens.pop().ok_or(ParseError::EndOfFile)?.as_str() {
18 "serverbound" => Ok(Serverbound),
19 "clientbound" => Ok(Clientbound),
20 token => Err(ParseError::InvalidToken {
21 token: token.to_string(),
22 error: "Invalid direction".to_string(),
23 }),
24 }
25 }
26}
27
28#[derive(Debug, Clone, PartialEq)]
29pub struct Packet {
30 pub name: Identifier,
31 pub direction: Direction,
32 pub state: Identifier,
33 pub id: u32,
34 pub fields: FieldList,
35}
36impl Parse for Packet {
37 fn parse(tokens: &mut Vec<String>) -> Result<Self, ParseError> {
38 ensure_tokens!(tokens, "packet");
39 let name: Identifier = Identifier::parse(tokens)?;
40 ensure_tokens!(tokens, "(");
41 let direction: Direction = Direction::parse(tokens)?;
42 ensure_tokens!(tokens, ",");
43 let state: Identifier = Identifier::parse(tokens)?;
44 ensure_tokens!(tokens, ",");
45 let id: String = tokens.pop().ok_or(ParseError::EndOfFile)?;
46 let id: u32 = strtoint(&id).map_err(|_| ParseError::InvalidToken {
47 token: id,
48 error: "Invalid packet id".to_string(),
49 })?;
50 ensure_tokens!(tokens, ")");
51 ensure_tokens!(tokens, "{");
52 let fields: FieldList = FieldList::parse(tokens)?;
53 ensure_tokens!(tokens, "}");
54
55 Ok(Packet {
56 name,
57 direction,
58 state,
59 id,
60 fields,
61 })
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use std::collections::HashSet;
68
69 use specmc_base::tokenize;
70
71 use crate::{
72 base::{BaseType, Field, IntegerType, Value},
73 test_parse,
74 types::Type,
75 };
76
77 use super::*;
78
79 #[test]
80 fn test_direction() {
81 let mut tokens: Vec<String> = tokenize!("serverbound clientbound unknown");
82
83 test_parse!(tokens, Direction, Ok(Direction::Serverbound));
84 test_parse!(tokens, Direction, Ok(Direction::Clientbound));
85
86 test_parse!(
87 tokens,
88 Direction,
89 Err(ParseError::InvalidToken {
90 token: "unknown".to_string(),
91 error: "Invalid direction".to_string()
92 })
93 );
94 assert!(tokens.is_empty());
95 test_parse!(tokens, Direction, Err(ParseError::EndOfFile));
96 }
97
98 #[test]
99 fn test_packet() {
100 let mut tokens: Vec<String> = tokenize!(
101 "
102 packet TestPacket(serverbound, Play, 0x42) {
103 i32 number
104 String message
105 bool flag
106 if (flag) {
107 i32 other
108 }
109 VarInt length = len(data)
110 List[u8] data
111 }
112 packet MalformedPacket(serverbound, Play, 0x42) {
113 i32 number
114 String message
115 "
116 );
117
118 test_parse!(
119 tokens,
120 Packet,
121 Ok(Packet {
122 name: Identifier("TestPacket".to_string()),
123 direction: Direction::Serverbound,
124 state: Identifier("Play".to_string()),
125 id: 66,
126 fields: FieldList(vec![
127 Field {
128 ty: Type::BaseType(BaseType::Integer(IntegerType::I32)),
129 name: Identifier("number".to_string()),
130 value: None,
131 conditions: HashSet::new(),
132 },
133 Field {
134 ty: Type::BaseType(BaseType::String { length: None }),
135 name: Identifier("message".to_string()),
136 value: None,
137 conditions: HashSet::new(),
138 },
139 Field {
140 ty: Type::BaseType(BaseType::Bool),
141 name: Identifier("flag".to_string()),
142 value: None,
143 conditions: HashSet::new(),
144 },
145 Field {
146 ty: Type::BaseType(BaseType::Integer(IntegerType::I32)),
147 name: Identifier("other".to_string()),
148 value: None,
149 conditions: HashSet::from_iter(vec!["flag".to_string()]),
150 },
151 Field {
152 ty: Type::BaseType(BaseType::Integer(IntegerType::VarInt)),
153 name: Identifier("length".to_string()),
154 value: Some(Value::Length(Identifier("data".to_string()))),
155 conditions: HashSet::new(),
156 },
157 Field {
158 ty: Type::BaseType(BaseType::List {
159 ty: Box::new(Type::BaseType(BaseType::Integer(IntegerType::U8))),
160 length: None,
161 }),
162 name: Identifier("data".to_string()),
163 value: None,
164 conditions: HashSet::new(),
165 },
166 ])
167 })
168 );
169
170 test_parse!(tokens, Packet, Err(ParseError::EndOfFile));
171 assert!(tokens.is_empty());
172 }
173}