author | alfadur |
Mon, 07 Oct 2019 23:53:47 +0300 | |
changeset 15444 | c03b2e263488 |
parent 15124 | 824472aa4d97 |
child 15801 | f57a3d48072b |
permissions | -rw-r--r-- |
13431
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
1 |
/** The parsers for the chat and multiplayer protocol. The main parser is `message`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
2 |
* # Protocol |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
3 |
* All messages consist of `\n`-separated strings. The end of a message is |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
4 |
* indicated by a double newline - `\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
5 |
* |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
6 |
* For example, a nullary command like PING will be actually sent as `PING\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
7 |
* A unary command, such as `START_GAME nick` will be actually sent as `START_GAME\nnick\n\n`. |
6a818f9192f4
Implement parsing for rnd and add a little documentation
Marcin Mielniczuk <marmistrz.dev@zoho.eu>
parents:
13423
diff
changeset
|
8 |
*/ |
15114 | 9 |
use nom::{ |
10 |
branch::alt, |
|
11 |
bytes::complete::{tag, tag_no_case, take_until, take_while}, |
|
12 |
character::complete::{newline, not_line_ending}, |
|
15118 | 13 |
combinator::{map, peek}, |
15114 | 14 |
error::{ErrorKind, ParseError}, |
15 |
multi::separated_list, |
|
15124 | 16 |
sequence::{delimited, pair, preceded, terminated, tuple}, |
15114 | 17 |
Err, IResult, |
18 |
}; |
|
19 |
||
14775 | 20 |
use std::{ |
21 |
num::ParseIntError, |
|
22 |
ops::Range, |
|
23 |
str, |
|
24 |
str::{FromStr, Utf8Error}, |
|
25 |
}; |
|
12133 | 26 |
|
15075 | 27 |
use super::messages::{HwProtocolMessage, HwProtocolMessage::*}; |
15074 | 28 |
use crate::core::types::{ |
14783 | 29 |
GameCfg, HedgehogInfo, ServerVar, TeamInfo, VoteType, MAX_HEDGEHOGS_PER_TEAM, |
30 |
}; |
|
14775 | 31 |
|
32 |
#[derive(Debug, PartialEq)] |
|
15075 | 33 |
pub struct HwProtocolError {} |
14775 | 34 |
|
15075 | 35 |
impl HwProtocolError { |
14775 | 36 |
fn new() -> Self { |
15075 | 37 |
HwProtocolError {} |
14775 | 38 |
} |
39 |
} |
|
40 |
||
15075 | 41 |
impl<I> ParseError<I> for HwProtocolError { |
14775 | 42 |
fn from_error_kind(input: I, kind: ErrorKind) -> Self { |
15075 | 43 |
HwProtocolError::new() |
14775 | 44 |
} |
45 |
||
46 |
fn append(input: I, kind: ErrorKind, other: Self) -> Self { |
|
15075 | 47 |
HwProtocolError::new() |
14775 | 48 |
} |
49 |
} |
|
50 |
||
15075 | 51 |
impl From<Utf8Error> for HwProtocolError { |
14775 | 52 |
fn from(_: Utf8Error) -> Self { |
15075 | 53 |
HwProtocolError::new() |
14775 | 54 |
} |
55 |
} |
|
56 |
||
15075 | 57 |
impl From<ParseIntError> for HwProtocolError { |
14775 | 58 |
fn from(_: ParseIntError) -> Self { |
15075 | 59 |
HwProtocolError::new() |
14775 | 60 |
} |
61 |
} |
|
62 |
||
15075 | 63 |
pub type HwResult<'a, O> = IResult<&'a [u8], O, HwProtocolError>; |
14775 | 64 |
|
15075 | 65 |
fn end_of_message(input: &[u8]) -> HwResult<&[u8]> { |
14775 | 66 |
tag("\n\n")(input) |
67 |
} |
|
68 |
||
15075 | 69 |
fn convert_utf8(input: &[u8]) -> HwResult<&str> { |
14775 | 70 |
match str::from_utf8(input) { |
71 |
Ok(str) => Ok((b"", str)), |
|
72 |
Err(utf_err) => Result::Err(Err::Failure(utf_err.into())), |
|
73 |
} |
|
74 |
} |
|
75 |
||
15075 | 76 |
fn convert_from_str<T>(str: &str) -> HwResult<T> |
14775 | 77 |
where |
78 |
T: FromStr<Err = ParseIntError>, |
|
79 |
{ |
|
80 |
match T::from_str(str) { |
|
81 |
Ok(x) => Ok((b"", x)), |
|
82 |
Err(format_err) => Result::Err(Err::Failure(format_err.into())), |
|
83 |
} |
|
84 |
} |
|
85 |
||
15075 | 86 |
fn str_line(input: &[u8]) -> HwResult<&str> { |
15116 | 87 |
let (i, text) = not_line_ending(input.clone())?; |
88 |
if i != input { |
|
89 |
Ok((i, convert_utf8(text)?.1)) |
|
90 |
} else { |
|
91 |
Err(Err::Error(HwProtocolError::new())) |
|
92 |
} |
|
14775 | 93 |
} |
94 |
||
15075 | 95 |
fn a_line(input: &[u8]) -> HwResult<String> { |
15118 | 96 |
map(str_line, String::from)(input) |
14775 | 97 |
} |
98 |
||
15075 | 99 |
fn cmd_arg(input: &[u8]) -> HwResult<String> { |
14775 | 100 |
let delimiters = b" \n"; |
15116 | 101 |
let (i, str) = take_while(move |c| !delimiters.contains(&c))(input.clone())?; |
102 |
if i != input { |
|
103 |
Ok((i, convert_utf8(str)?.1.to_string())) |
|
104 |
} else { |
|
105 |
Err(Err::Error(HwProtocolError::new())) |
|
106 |
} |
|
14775 | 107 |
} |
108 |
||
15075 | 109 |
fn u8_line(input: &[u8]) -> HwResult<u8> { |
14775 | 110 |
let (i, str) = str_line(input)?; |
111 |
Ok((i, convert_from_str(str)?.1)) |
|
112 |
} |
|
113 |
||
15075 | 114 |
fn u16_line(input: &[u8]) -> HwResult<u16> { |
14775 | 115 |
let (i, str) = str_line(input)?; |
116 |
Ok((i, convert_from_str(str)?.1)) |
|
117 |
} |
|
118 |
||
15075 | 119 |
fn u32_line(input: &[u8]) -> HwResult<u32> { |
14775 | 120 |
let (i, str) = str_line(input)?; |
121 |
Ok((i, convert_from_str(str)?.1)) |
|
122 |
} |
|
123 |
||
15075 | 124 |
fn yes_no_line(input: &[u8]) -> HwResult<bool> { |
14775 | 125 |
alt(( |
15118 | 126 |
map(tag_no_case(b"YES"), |_| true), |
127 |
map(tag_no_case(b"NO"), |_| false), |
|
14775 | 128 |
))(input) |
129 |
} |
|
130 |
||
15075 | 131 |
fn opt_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
14775 | 132 |
alt(( |
15118 | 133 |
map(peek(end_of_message), |_| None), |
134 |
map(preceded(tag("\n"), a_line), Some), |
|
14775 | 135 |
))(input) |
136 |
} |
|
137 |
||
15075 | 138 |
fn spaces(input: &[u8]) -> HwResult<&[u8]> { |
15118 | 139 |
preceded(tag(" "), take_while(|c| c == b' '))(input) |
14775 | 140 |
} |
141 |
||
15075 | 142 |
fn opt_space_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
14775 | 143 |
alt(( |
15118 | 144 |
map(peek(end_of_message), |_| None), |
145 |
map(preceded(spaces, a_line), Some), |
|
14775 | 146 |
))(input) |
147 |
} |
|
12134 | 148 |
|
15075 | 149 |
fn hedgehog_array(input: &[u8]) -> HwResult<[HedgehogInfo; 8]> { |
150 |
fn hedgehog_line(input: &[u8]) -> HwResult<HedgehogInfo> { |
|
15124 | 151 |
map( |
152 |
tuple((terminated(a_line, newline), a_line)), |
|
153 |
|(name, hat)| HedgehogInfo { name, hat }, |
|
154 |
)(input) |
|
14775 | 155 |
} |
156 |
||
15124 | 157 |
let (i, (h1, h2, h3, h4, h5, h6, h7, h8)) = tuple(( |
158 |
terminated(hedgehog_line, newline), |
|
159 |
terminated(hedgehog_line, newline), |
|
160 |
terminated(hedgehog_line, newline), |
|
161 |
terminated(hedgehog_line, newline), |
|
162 |
terminated(hedgehog_line, newline), |
|
163 |
terminated(hedgehog_line, newline), |
|
164 |
terminated(hedgehog_line, newline), |
|
165 |
hedgehog_line, |
|
166 |
))(input)?; |
|
14775 | 167 |
|
168 |
Ok((i, [h1, h2, h3, h4, h5, h6, h7, h8])) |
|
169 |
} |
|
12133 | 170 |
|
15075 | 171 |
fn voting(input: &[u8]) -> HwResult<VoteType> { |
14775 | 172 |
alt(( |
15118 | 173 |
map(tag_no_case("PAUSE"), |_| VoteType::Pause), |
174 |
map(tag_no_case("NEWSEED"), |_| VoteType::NewSeed), |
|
175 |
map( |
|
176 |
preceded(pair(tag_no_case("KICK"), spaces), a_line), |
|
177 |
VoteType::Kick, |
|
178 |
), |
|
179 |
map( |
|
180 |
preceded(pair(tag_no_case("HEDGEHOGS"), spaces), u8_line), |
|
181 |
VoteType::HedgehogsPerTeam, |
|
182 |
), |
|
15119 | 183 |
map(preceded(tag_no_case("MAP"), opt_space_arg), VoteType::Map), |
14775 | 184 |
))(input) |
185 |
} |
|
12135 | 186 |
|
15075 | 187 |
fn no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
15119 | 188 |
fn message<'a>( |
14775 | 189 |
name: &'a str, |
15075 | 190 |
msg: HwProtocolMessage, |
15119 | 191 |
) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> { |
192 |
move |i| map(tag(name), |_| msg.clone())(i) |
|
14775 | 193 |
} |
194 |
||
195 |
alt(( |
|
15119 | 196 |
message("PING", Ping), |
197 |
message("PONG", Pong), |
|
198 |
message("LIST", List), |
|
199 |
message("BANLIST", BanList), |
|
200 |
message("GET_SERVER_VAR", GetServerVar), |
|
201 |
message("TOGGLE_READY", ToggleReady), |
|
202 |
message("START_GAME", StartGame), |
|
203 |
message("TOGGLE_RESTRICT_JOINS", ToggleRestrictJoin), |
|
204 |
message("TOGGLE_RESTRICT_TEAMS", ToggleRestrictTeams), |
|
205 |
message("TOGGLE_REGISTERED_ONLY", ToggleRegisteredOnly), |
|
14775 | 206 |
))(input) |
207 |
} |
|
12135 | 208 |
|
15075 | 209 |
fn single_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
15119 | 210 |
fn message<'a, T, F, G>( |
14775 | 211 |
name: &'a str, |
212 |
parser: F, |
|
213 |
constructor: G, |
|
15119 | 214 |
) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> |
14775 | 215 |
where |
15075 | 216 |
F: Fn(&[u8]) -> HwResult<T>, |
217 |
G: Fn(T) -> HwProtocolMessage, |
|
14775 | 218 |
{ |
15119 | 219 |
map(preceded(tag(name), parser), constructor) |
14775 | 220 |
} |
221 |
||
222 |
alt(( |
|
15119 | 223 |
message("NICK\n", a_line, Nick), |
224 |
message("INFO\n", a_line, Info), |
|
225 |
message("CHAT\n", a_line, Chat), |
|
226 |
message("PART", opt_arg, Part), |
|
227 |
message("FOLLOW\n", a_line, Follow), |
|
228 |
message("KICK\n", a_line, Kick), |
|
229 |
message("UNBAN\n", a_line, Unban), |
|
230 |
message("EM\n", a_line, EngineMessage), |
|
231 |
message("TEAMCHAT\n", a_line, TeamChat), |
|
232 |
message("ROOM_NAME\n", a_line, RoomName), |
|
233 |
message("REMOVE_TEAM\n", a_line, RemoveTeam), |
|
234 |
message("ROUNDFINISHED", opt_arg, |_| RoundFinished), |
|
235 |
message("PROTO\n", u16_line, Proto), |
|
236 |
message("QUIT", opt_arg, Quit), |
|
14775 | 237 |
))(input) |
238 |
} |
|
12135 | 239 |
|
15075 | 240 |
fn cmd_message<'a>(input: &'a [u8]) -> HwResult<'a, HwProtocolMessage> { |
15119 | 241 |
fn cmd_no_arg<'a>( |
14775 | 242 |
name: &'a str, |
15075 | 243 |
msg: HwProtocolMessage, |
15119 | 244 |
) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> { |
245 |
move |i| map(tag_no_case(name), |_| msg.clone())(i) |
|
14775 | 246 |
} |
247 |
||
15119 | 248 |
fn cmd_single_arg<'a, T, F, G>( |
14775 | 249 |
name: &'a str, |
250 |
parser: F, |
|
251 |
constructor: G, |
|
15119 | 252 |
) -> impl Fn(&'a [u8]) -> HwResult<'a, HwProtocolMessage> |
14775 | 253 |
where |
15075 | 254 |
F: Fn(&'a [u8]) -> HwResult<'a, T>, |
255 |
G: Fn(T) -> HwProtocolMessage, |
|
14775 | 256 |
{ |
15118 | 257 |
map( |
258 |
preceded(pair(tag_no_case(name), spaces), parser), |
|
259 |
constructor, |
|
15119 | 260 |
) |
14775 | 261 |
} |
262 |
||
15075 | 263 |
fn cmd_no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
14775 | 264 |
alt(( |
15119 | 265 |
cmd_no_arg("STATS", Stats), |
266 |
cmd_no_arg("FIX", Fix), |
|
267 |
cmd_no_arg("UNFIX", Unfix), |
|
268 |
cmd_no_arg("REGISTERED_ONLY", ToggleServerRegisteredOnly), |
|
269 |
cmd_no_arg("SUPER_POWER", SuperPower), |
|
14775 | 270 |
))(input) |
271 |
} |
|
12135 | 272 |
|
15075 | 273 |
fn cmd_single_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
14775 | 274 |
alt(( |
15119 | 275 |
cmd_single_arg("RESTART_SERVER", |i| tag("YES")(i), |_| RestartServer), |
276 |
cmd_single_arg("DELEGATE", a_line, Delegate), |
|
277 |
cmd_single_arg("DELETE", a_line, Delete), |
|
278 |
cmd_single_arg("SAVEROOM", a_line, SaveRoom), |
|
279 |
cmd_single_arg("LOADROOM", a_line, LoadRoom), |
|
280 |
cmd_single_arg("GLOBAL", a_line, Global), |
|
281 |
cmd_single_arg("WATCH", u32_line, Watch), |
|
282 |
cmd_single_arg("VOTE", yes_no_line, Vote), |
|
283 |
cmd_single_arg("FORCE", yes_no_line, ForceVote), |
|
284 |
cmd_single_arg("INFO", a_line, Info), |
|
285 |
cmd_single_arg("MAXTEAMS", u8_line, MaxTeams), |
|
286 |
cmd_single_arg("CALLVOTE", voting, |v| CallVote(Some(v))), |
|
14775 | 287 |
))(input) |
288 |
} |
|
289 |
||
15118 | 290 |
preceded( |
291 |
tag("CMD\n"), |
|
14775 | 292 |
alt(( |
293 |
cmd_no_arg_message, |
|
294 |
cmd_single_arg_message, |
|
15118 | 295 |
map(tag_no_case("CALLVOTE"), |_| CallVote(None)), |
15119 | 296 |
map(preceded(tag_no_case("GREETING"), opt_space_arg), Greeting), |
15118 | 297 |
map(preceded(tag_no_case("PART"), opt_space_arg), Part), |
298 |
map(preceded(tag_no_case("QUIT"), opt_space_arg), Quit), |
|
299 |
map( |
|
300 |
preceded( |
|
301 |
tag_no_case("SAVE"), |
|
302 |
pair(preceded(spaces, cmd_arg), preceded(spaces, cmd_arg)), |
|
303 |
), |
|
304 |
|(n, l)| Save(n, l), |
|
305 |
), |
|
306 |
map( |
|
307 |
preceded( |
|
308 |
tag_no_case("RND"), |
|
309 |
alt(( |
|
310 |
map(peek(end_of_message), |_| vec![]), |
|
311 |
preceded(spaces, separated_list(spaces, cmd_arg)), |
|
312 |
)), |
|
313 |
), |
|
314 |
Rnd, |
|
315 |
), |
|
14775 | 316 |
)), |
15118 | 317 |
)(input) |
14775 | 318 |
} |
319 |
||
15075 | 320 |
fn config_message<'a>(input: &'a [u8]) -> HwResult<'a, HwProtocolMessage> { |
15119 | 321 |
fn cfg_single_arg<'a, T, F, G>( |
14775 | 322 |
name: &'a str, |
323 |
parser: F, |
|
324 |
constructor: G, |
|
15119 | 325 |
) -> impl Fn(&'a [u8]) -> HwResult<'a, GameCfg> |
14775 | 326 |
where |
15075 | 327 |
F: Fn(&[u8]) -> HwResult<T>, |
14775 | 328 |
G: Fn(T) -> GameCfg, |
329 |
{ |
|
15119 | 330 |
map(preceded(pair(tag(name), newline), parser), constructor) |
14775 | 331 |
} |
332 |
||
15118 | 333 |
let (i, cfg) = preceded( |
334 |
tag("CFG\n"), |
|
14775 | 335 |
alt(( |
15119 | 336 |
cfg_single_arg("THEME", a_line, GameCfg::Theme), |
337 |
cfg_single_arg("SCRIPT", a_line, GameCfg::Script), |
|
338 |
cfg_single_arg("MAP", a_line, GameCfg::MapType), |
|
339 |
cfg_single_arg("MAPGEN", u32_line, GameCfg::MapGenerator), |
|
340 |
cfg_single_arg("MAZE_SIZE", u32_line, GameCfg::MazeSize), |
|
341 |
cfg_single_arg("TEMPLATE", u32_line, GameCfg::Template), |
|
342 |
cfg_single_arg("FEATURE_SIZE", u32_line, GameCfg::FeatureSize), |
|
343 |
cfg_single_arg("SEED", a_line, GameCfg::Seed), |
|
344 |
cfg_single_arg("DRAWNMAP", a_line, GameCfg::DrawnMap), |
|
15118 | 345 |
preceded(pair(tag("AMMO"), newline), |i| { |
346 |
let (i, name) = a_line(i)?; |
|
347 |
let (i, value) = opt_arg(i)?; |
|
348 |
Ok((i, GameCfg::Ammo(name, value))) |
|
349 |
}), |
|
15124 | 350 |
preceded( |
351 |
pair(tag("SCHEME"), newline), |
|
352 |
map( |
|
353 |
pair( |
|
354 |
a_line, |
|
355 |
alt(( |
|
356 |
map(peek(end_of_message), |_| None), |
|
357 |
map(preceded(newline, separated_list(newline, a_line)), Some), |
|
358 |
)), |
|
359 |
), |
|
360 |
|(name, values)| GameCfg::Scheme(name, values.unwrap_or_default()), |
|
361 |
), |
|
362 |
), |
|
14775 | 363 |
)), |
15118 | 364 |
)(input)?; |
14775 | 365 |
Ok((i, Cfg(cfg))) |
366 |
} |
|
12133 | 367 |
|
15075 | 368 |
fn server_var_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
15118 | 369 |
map( |
370 |
preceded( |
|
371 |
tag("SET_SERVER_VAR\n"), |
|
372 |
alt(( |
|
373 |
map(preceded(tag("MOTD_NEW\n"), a_line), ServerVar::MOTDNew), |
|
374 |
map(preceded(tag("MOTD_OLD\n"), a_line), ServerVar::MOTDOld), |
|
375 |
map( |
|
376 |
preceded(tag("LATEST_PROTO\n"), u16_line), |
|
377 |
ServerVar::LatestProto, |
|
378 |
), |
|
379 |
)), |
|
380 |
), |
|
381 |
SetServerVar, |
|
382 |
)(input) |
|
14783 | 383 |
} |
384 |
||
15075 | 385 |
fn complex_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
14775 | 386 |
alt(( |
15124 | 387 |
preceded( |
388 |
pair(tag("PASSWORD"), newline), |
|
389 |
map(pair(terminated(a_line, newline), a_line), |(pass, salt)| { |
|
390 |
Password(pass, salt) |
|
391 |
}), |
|
392 |
), |
|
393 |
preceded( |
|
394 |
pair(tag("CHECKER"), newline), |
|
395 |
map( |
|
396 |
tuple(( |
|
397 |
terminated(u16_line, newline), |
|
398 |
terminated(a_line, newline), |
|
399 |
a_line, |
|
400 |
)), |
|
401 |
|(protocol, name, pass)| Checker(protocol, name, pass), |
|
402 |
), |
|
403 |
), |
|
404 |
preceded( |
|
405 |
pair(tag("CREATE_ROOM"), newline), |
|
406 |
map(pair(a_line, opt_arg), |(name, pass)| CreateRoom(name, pass)), |
|
407 |
), |
|
408 |
preceded( |
|
409 |
pair(tag("JOIN_ROOM"), newline), |
|
410 |
map(pair(a_line, opt_arg), |(name, pass)| JoinRoom(name, pass)), |
|
411 |
), |
|
412 |
preceded( |
|
413 |
pair(tag("ADD_TEAM"), newline), |
|
414 |
map( |
|
415 |
tuple(( |
|
416 |
terminated(a_line, newline), |
|
417 |
terminated(u8_line, newline), |
|
418 |
terminated(a_line, newline), |
|
419 |
terminated(a_line, newline), |
|
420 |
terminated(a_line, newline), |
|
421 |
terminated(a_line, newline), |
|
422 |
terminated(u8_line, newline), |
|
423 |
hedgehog_array, |
|
424 |
)), |
|
425 |
|(name, color, grave, fort, voice_pack, flag, difficulty, hedgehogs)| { |
|
426 |
AddTeam(Box::new(TeamInfo { |
|
427 |
owner: String::new(), |
|
428 |
name, |
|
429 |
color, |
|
430 |
grave, |
|
431 |
fort, |
|
432 |
voice_pack, |
|
433 |
flag, |
|
434 |
difficulty, |
|
435 |
hedgehogs, |
|
436 |
hedgehogs_number: 0, |
|
437 |
})) |
|
438 |
}, |
|
439 |
), |
|
440 |
), |
|
441 |
preceded( |
|
442 |
pair(tag("HH_NUM"), newline), |
|
443 |
map( |
|
444 |
pair(terminated(a_line, newline), u8_line), |
|
445 |
|(name, count)| SetHedgehogsNumber(name, count), |
|
446 |
), |
|
447 |
), |
|
448 |
preceded( |
|
449 |
pair(tag("TEAM_COLOR"), newline), |
|
450 |
map( |
|
451 |
pair(terminated(a_line, newline), u8_line), |
|
452 |
|(name, color)| SetTeamColor(name, color), |
|
453 |
), |
|
454 |
), |
|
455 |
preceded( |
|
456 |
pair(tag("BAN"), newline), |
|
457 |
map( |
|
458 |
tuple(( |
|
459 |
terminated(a_line, newline), |
|
460 |
terminated(a_line, newline), |
|
461 |
u32_line, |
|
462 |
)), |
|
463 |
|(name, reason, time)| Ban(name, reason, time), |
|
464 |
), |
|
465 |
), |
|
466 |
preceded( |
|
467 |
pair(tag("BAN_IP"), newline), |
|
468 |
map( |
|
469 |
tuple(( |
|
470 |
terminated(a_line, newline), |
|
471 |
terminated(a_line, newline), |
|
472 |
u32_line, |
|
473 |
)), |
|
474 |
|(ip, reason, time)| BanIp(ip, reason, time), |
|
475 |
), |
|
476 |
), |
|
477 |
preceded( |
|
478 |
pair(tag("BAN_NICK"), newline), |
|
479 |
map( |
|
480 |
tuple(( |
|
481 |
terminated(a_line, newline), |
|
482 |
terminated(a_line, newline), |
|
483 |
u32_line, |
|
484 |
)), |
|
485 |
|(nick, reason, time)| BanNick(nick, reason, time), |
|
486 |
), |
|
487 |
), |
|
14775 | 488 |
))(input) |
489 |
} |
|
13422 | 490 |
|
15075 | 491 |
pub fn malformed_message(input: &[u8]) -> HwResult<()> { |
15118 | 492 |
map(terminated(take_until(&b"\n\n"[..]), end_of_message), |_| ())(input) |
14775 | 493 |
} |
12137
193dfdcb0620
- Use logging facilities instead of plain println!
unc0rr
parents:
12136
diff
changeset
|
494 |
|
15075 | 495 |
pub fn message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
15122 | 496 |
delimited( |
15118 | 497 |
take_while(|c| c == b'\n'), |
15122 | 498 |
alt(( |
499 |
no_arg_message, |
|
500 |
single_arg_message, |
|
501 |
cmd_message, |
|
502 |
config_message, |
|
503 |
server_var_message, |
|
504 |
complex_message, |
|
505 |
)), |
|
506 |
end_of_message, |
|
15118 | 507 |
)(input) |
14775 | 508 |
} |
12133 | 509 |
|
13796 | 510 |
#[cfg(test)] |
14777 | 511 |
mod test { |
15112 | 512 |
use super::message; |
15116 | 513 |
use crate::{ |
514 |
core::types::GameCfg, |
|
515 |
protocol::{messages::HwProtocolMessage::*, parser::HwProtocolError, test::gen_proto_msg}, |
|
15112 | 516 |
}; |
14777 | 517 |
use proptest::{proptest, proptest_helper}; |
518 |
||
519 |
#[cfg(test)] |
|
520 |
proptest! { |
|
521 |
#[test] |
|
522 |
fn is_parser_composition_idempotent(ref msg in gen_proto_msg()) { |
|
523 |
println!("!! Msg: {:?}, Bytes: {:?} !!", msg, msg.to_raw_protocol().as_bytes()); |
|
524 |
assert_eq!(message(msg.to_raw_protocol().as_bytes()), Ok((&b""[..], msg.clone()))) |
|
525 |
} |
|
526 |
} |
|
527 |
||
13119
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
528 |
#[test] |
14777 | 529 |
fn parse_test() { |
530 |
assert_eq!(message(b"PING\n\n"), Ok((&b""[..], Ping))); |
|
531 |
assert_eq!(message(b"START_GAME\n\n"), Ok((&b""[..], StartGame))); |
|
532 |
assert_eq!( |
|
533 |
message(b"NICK\nit's me\n\n"), |
|
534 |
Ok((&b""[..], Nick("it's me".to_string()))) |
|
535 |
); |
|
536 |
assert_eq!(message(b"PROTO\n51\n\n"), Ok((&b""[..], Proto(51)))); |
|
537 |
assert_eq!( |
|
538 |
message(b"QUIT\nbye-bye\n\n"), |
|
539 |
Ok((&b""[..], Quit(Some("bye-bye".to_string())))) |
|
540 |
); |
|
541 |
assert_eq!(message(b"QUIT\n\n"), Ok((&b""[..], Quit(None)))); |
|
542 |
assert_eq!( |
|
14795 | 543 |
message(b"CMD\nwatch 49471\n\n"), |
544 |
Ok((&b""[..], Watch(49471))) |
|
14777 | 545 |
); |
546 |
assert_eq!( |
|
547 |
message(b"BAN\nme\nbad\n77\n\n"), |
|
548 |
Ok((&b""[..], Ban("me".to_string(), "bad".to_string(), 77))) |
|
549 |
); |
|
550 |
||
551 |
assert_eq!(message(b"CMD\nPART\n\n"), Ok((&b""[..], Part(None)))); |
|
552 |
assert_eq!( |
|
553 |
message(b"CMD\nPART _msg_\n\n"), |
|
554 |
Ok((&b""[..], Part(Some("_msg_".to_string())))) |
|
555 |
); |
|
556 |
||
557 |
assert_eq!(message(b"CMD\nRND\n\n"), Ok((&b""[..], Rnd(vec![])))); |
|
558 |
assert_eq!( |
|
559 |
message(b"CMD\nRND A B\n\n"), |
|
560 |
Ok((&b""[..], Rnd(vec![String::from("A"), String::from("B")]))) |
|
561 |
); |
|
562 |
||
563 |
assert_eq!( |
|
15116 | 564 |
message(b"CFG\nSCHEME\na\nA\n\n"), |
565 |
Ok(( |
|
566 |
&b""[..], |
|
567 |
Cfg(GameCfg::Scheme("a".to_string(), vec!["A".to_string()])) |
|
568 |
)) |
|
569 |
); |
|
570 |
||
571 |
assert_eq!( |
|
14795 | 572 |
message(b"QUIT\n1\n2\n\n"), |
15075 | 573 |
Err(nom::Err::Error(HwProtocolError::new())) |
14777 | 574 |
); |
13119
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
575 |
} |
1e39b8749072
separated the server logic from all the async io mess.
alfadur
parents:
12142
diff
changeset
|
576 |
} |