130 Ok((i, convert_from_str(str)?.1)) |
121 Ok((i, convert_from_str(str)?.1)) |
131 } |
122 } |
132 |
123 |
133 fn yes_no_line(input: &[u8]) -> HwResult<bool> { |
124 fn yes_no_line(input: &[u8]) -> HwResult<bool> { |
134 alt(( |
125 alt(( |
135 |i| tag_no_case(b"YES")(i).map(|(i, _)| (i, true)), |
126 map(tag_no_case(b"YES"), |_| true), |
136 |i| tag_no_case(b"NO")(i).map(|(i, _)| (i, false)), |
127 map(tag_no_case(b"NO"), |_| false), |
137 ))(input) |
128 ))(input) |
138 } |
129 } |
139 |
130 |
140 fn opt_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
131 fn opt_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
141 alt(( |
132 alt(( |
142 |i| peek(end_of_message)(i).map(|(i, _)| (i, None)), |
133 map(peek(end_of_message), |_| None), |
143 |i| precededc(i, hw_tag("\n"), a_line).map(|(i, v)| (i, Some(v))), |
134 map(preceded(tag("\n"), a_line), Some), |
144 ))(input) |
135 ))(input) |
145 } |
136 } |
146 |
137 |
147 fn spaces(input: &[u8]) -> HwResult<&[u8]> { |
138 fn spaces(input: &[u8]) -> HwResult<&[u8]> { |
148 precededc(input, hw_tag(" "), |i| take_while(|c| c == b' ')(i)) |
139 preceded(tag(" "), take_while(|c| c == b' '))(input) |
149 } |
140 } |
150 |
141 |
151 fn opt_space_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
142 fn opt_space_arg<'a>(input: &'a [u8]) -> HwResult<'a, Option<String>> { |
152 alt(( |
143 alt(( |
153 |i| peek(end_of_message)(i).map(|(i, _)| (i, None)), |
144 map(peek(end_of_message), |_| None), |
154 |i| precededc(i, spaces, a_line).map(|(i, v)| (i, Some(v))), |
145 map(preceded(spaces, a_line), Some), |
155 ))(input) |
146 ))(input) |
156 } |
147 } |
157 |
148 |
158 fn hedgehog_array(input: &[u8]) -> HwResult<[HedgehogInfo; 8]> { |
149 fn hedgehog_array(input: &[u8]) -> HwResult<[HedgehogInfo; 8]> { |
159 fn hedgehog_line(input: &[u8]) -> HwResult<HedgehogInfo> { |
150 fn hedgehog_line(input: &[u8]) -> HwResult<HedgehogInfo> { |
160 let (i, name) = terminatedc(input, a_line, newline)?; |
151 let (i, name) = terminated(a_line, newline)(input)?; |
161 let (i, hat) = a_line(i)?; |
152 let (i, hat) = a_line(i)?; |
162 Ok((i, HedgehogInfo { name, hat })) |
153 Ok((i, HedgehogInfo { name, hat })) |
163 } |
154 } |
164 |
155 |
165 let (i, h1) = terminatedc(input, hedgehog_line, newline)?; |
156 let (i, h1) = terminated(hedgehog_line, newline)(input)?; |
166 let (i, h2) = terminatedc(i, hedgehog_line, newline)?; |
157 let (i, h2) = terminated(hedgehog_line, newline)(i)?; |
167 let (i, h3) = terminatedc(i, hedgehog_line, newline)?; |
158 let (i, h3) = terminated(hedgehog_line, newline)(i)?; |
168 let (i, h4) = terminatedc(i, hedgehog_line, newline)?; |
159 let (i, h4) = terminated(hedgehog_line, newline)(i)?; |
169 let (i, h5) = terminatedc(i, hedgehog_line, newline)?; |
160 let (i, h5) = terminated(hedgehog_line, newline)(i)?; |
170 let (i, h6) = terminatedc(i, hedgehog_line, newline)?; |
161 let (i, h6) = terminated(hedgehog_line, newline)(i)?; |
171 let (i, h7) = terminatedc(i, hedgehog_line, newline)?; |
162 let (i, h7) = terminated(hedgehog_line, newline)(i)?; |
172 let (i, h8) = hedgehog_line(i)?; |
163 let (i, h8) = hedgehog_line(i)?; |
173 |
164 |
174 Ok((i, [h1, h2, h3, h4, h5, h6, h7, h8])) |
165 Ok((i, [h1, h2, h3, h4, h5, h6, h7, h8])) |
175 } |
166 } |
176 |
167 |
177 fn voting(input: &[u8]) -> HwResult<VoteType> { |
168 fn voting(input: &[u8]) -> HwResult<VoteType> { |
178 alt(( |
169 alt(( |
179 |i| tag_no_case("PAUSE")(i).map(|(i, _)| (i, VoteType::Pause)), |
170 map(tag_no_case("PAUSE"), |_| VoteType::Pause), |
180 |i| tag_no_case("NEWSEED")(i).map(|(i, _)| (i, VoteType::NewSeed)), |
171 map(tag_no_case("NEWSEED"), |_| VoteType::NewSeed), |
181 |i| { |
172 map( |
182 precededc(i, |i| precededc(i, hw_tag_no_case("KICK"), spaces), a_line) |
173 preceded(pair(tag_no_case("KICK"), spaces), a_line), |
183 .map(|(i, s)| (i, VoteType::Kick(s))) |
174 VoteType::Kick, |
184 }, |
175 ), |
185 |i| { |
176 map( |
186 precededc( |
177 preceded(pair(tag_no_case("HEDGEHOGS"), spaces), u8_line), |
187 i, |
178 VoteType::HedgehogsPerTeam, |
188 |i| precededc(i, hw_tag_no_case("HEDGEHOGS"), spaces), |
179 ), |
189 u8_line, |
180 map( |
190 ) |
181 preceded(tag_no_case("MAP"), opt_space_arg), |
191 .map(|(i, n)| (i, VoteType::HedgehogsPerTeam(n))) |
182 VoteType::Map, |
192 }, |
183 ), |
193 |i| precededc(i, hw_tag_no_case("MAP"), opt_space_arg).map(|(i, v)| (i, VoteType::Map(v))), |
|
194 ))(input) |
184 ))(input) |
195 } |
185 } |
196 |
186 |
197 fn no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
187 fn no_arg_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
198 fn messagec<'a>( |
188 fn messagec<'a>( |
199 input: &'a [u8], |
189 input: &'a [u8], |
200 name: &'a str, |
190 name: &'a str, |
201 msg: HwProtocolMessage, |
191 msg: HwProtocolMessage, |
202 ) -> HwResult<'a, HwProtocolMessage> { |
192 ) -> HwResult<'a, HwProtocolMessage> { |
203 tag(name)(input).map(|(i, _)| (i, msg.clone())) |
193 map(tag(name), |_| msg.clone())(input) |
204 } |
194 } |
205 |
195 |
206 alt(( |
196 alt(( |
207 |i| messagec(i, "PING", Ping), |
197 |i| messagec(i, "PING", Ping), |
208 |i| messagec(i, "PONG", Pong), |
198 |i| messagec(i, "PONG", Pong), |
347 ) -> HwResult<'a, GameCfg> |
333 ) -> HwResult<'a, GameCfg> |
348 where |
334 where |
349 F: Fn(&[u8]) -> HwResult<T>, |
335 F: Fn(&[u8]) -> HwResult<T>, |
350 G: Fn(T) -> GameCfg, |
336 G: Fn(T) -> GameCfg, |
351 { |
337 { |
352 precededc(input, |i| terminatedc(i, hw_tag(name), newline), parser) |
338 map(preceded(pair(tag(name), newline), parser), constructor)(input) |
353 .map(|(i, v)| (i, constructor(v))) |
339 } |
354 } |
340 |
355 |
341 let (i, cfg) = preceded( |
356 let (i, cfg) = precededc( |
342 tag("CFG\n"), |
357 input, |
|
358 hw_tag("CFG\n"), |
|
359 alt(( |
343 alt(( |
360 |i| cfgc_single_arg(i, "THEME", a_line, GameCfg::Theme), |
344 |i| cfgc_single_arg(i, "THEME", a_line, GameCfg::Theme), |
361 |i| cfgc_single_arg(i, "SCRIPT", a_line, GameCfg::Script), |
345 |i| cfgc_single_arg(i, "SCRIPT", a_line, GameCfg::Script), |
362 |i| cfgc_single_arg(i, "MAP", a_line, GameCfg::MapType), |
346 |i| cfgc_single_arg(i, "MAP", a_line, GameCfg::MapType), |
363 |i| cfgc_single_arg(i, "MAPGEN", u32_line, GameCfg::MapGenerator), |
347 |i| cfgc_single_arg(i, "MAPGEN", u32_line, GameCfg::MapGenerator), |
364 |i| cfgc_single_arg(i, "MAZE_SIZE", u32_line, GameCfg::MazeSize), |
348 |i| cfgc_single_arg(i, "MAZE_SIZE", u32_line, GameCfg::MazeSize), |
365 |i| cfgc_single_arg(i, "TEMPLATE", u32_line, GameCfg::Template), |
349 |i| cfgc_single_arg(i, "TEMPLATE", u32_line, GameCfg::Template), |
366 |i| cfgc_single_arg(i, "FEATURE_SIZE", u32_line, GameCfg::FeatureSize), |
350 |i| cfgc_single_arg(i, "FEATURE_SIZE", u32_line, GameCfg::FeatureSize), |
367 |i| cfgc_single_arg(i, "SEED", a_line, GameCfg::Seed), |
351 |i| cfgc_single_arg(i, "SEED", a_line, GameCfg::Seed), |
368 |i| cfgc_single_arg(i, "DRAWNMAP", a_line, GameCfg::DrawnMap), |
352 |i| cfgc_single_arg(i, "DRAWNMAP", a_line, GameCfg::DrawnMap), |
369 |i| { |
353 preceded(pair(tag("AMMO"), newline), |i| { |
370 precededc( |
354 let (i, name) = a_line(i)?; |
371 i, |
355 let (i, value) = opt_arg(i)?; |
372 |i| terminatedc(i, hw_tag("AMMO"), newline), |
356 Ok((i, GameCfg::Ammo(name, value))) |
373 |i| { |
357 }), |
374 let (i, name) = a_line(i)?; |
358 preceded(pair(tag("SCHEME"), newline), |i| { |
375 let (i, value) = opt_arg(i)?; |
359 let (i, name) = a_line(i)?; |
376 Ok((i, GameCfg::Ammo(name, value))) |
360 let (i, values) = alt(( |
377 }, |
361 map(peek(end_of_message), |_| None), |
378 ) |
362 map(preceded(newline, separated_list(newline, a_line)), Some), |
379 }, |
363 ))(i)?; |
380 |i| { |
364 Ok((i, GameCfg::Scheme(name, values.unwrap_or_default()))) |
381 precededc( |
365 }), |
382 i, |
|
383 |i| terminatedc(i, hw_tag("SCHEME"), newline), |
|
384 |i| { |
|
385 let (i, name) = a_line(i)?; |
|
386 let (i, values) = alt(( |
|
387 |i| peek(end_of_message)(i).map(|(i, _)| (i, None)), |
|
388 |i| { |
|
389 precededc(i, newline, |i| separated_list(newline, a_line)(i)) |
|
390 .map(|(i, v)| (i, Some(v))) |
|
391 }, |
|
392 ))(i)?; |
|
393 Ok((i, GameCfg::Scheme(name, values.unwrap_or_default()))) |
|
394 }, |
|
395 ) |
|
396 }, |
|
397 )), |
366 )), |
398 )?; |
367 )(input)?; |
399 Ok((i, Cfg(cfg))) |
368 Ok((i, Cfg(cfg))) |
400 } |
369 } |
401 |
370 |
402 fn server_var_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
371 fn server_var_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
403 precededc( |
372 map( |
404 input, |
373 preceded( |
405 hw_tag("SET_SERVER_VAR\n"), |
374 tag("SET_SERVER_VAR\n"), |
406 alt(( |
375 alt(( |
407 |i| { |
376 map(preceded(tag("MOTD_NEW\n"), a_line), ServerVar::MOTDNew), |
408 precededc(i, hw_tag("MOTD_NEW\n"), a_line) |
377 map(preceded(tag("MOTD_OLD\n"), a_line), ServerVar::MOTDOld), |
409 .map(|(i, s)| (i, SetServerVar(ServerVar::MOTDNew(s)))) |
378 map( |
410 }, |
379 preceded(tag("LATEST_PROTO\n"), u16_line), |
411 |i| { |
380 ServerVar::LatestProto, |
412 precededc(i, hw_tag("MOTD_OLD\n"), a_line) |
381 ), |
413 .map(|(i, s)| (i, SetServerVar(ServerVar::MOTDOld(s)))) |
382 )), |
414 }, |
383 ), |
415 |i| { |
384 SetServerVar, |
416 precededc(i, hw_tag("LATEST_PROTO\n"), u16_line) |
385 )(input) |
417 .map(|(i, n)| (i, SetServerVar(ServerVar::LatestProto(n)))) |
|
418 }, |
|
419 )), |
|
420 ) |
|
421 } |
386 } |
422 |
387 |
423 fn complex_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
388 fn complex_message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
424 alt(( |
389 alt(( |
425 |i| { |
390 preceded(pair(tag("PASSWORD"), newline), |i| { |
426 precededc( |
391 let (i, pass) = terminated(a_line, newline)(i)?; |
|
392 let (i, salt) = a_line(i)?; |
|
393 Ok((i, Password(pass, salt))) |
|
394 }), |
|
395 preceded(pair(tag("CHECKER"), newline), |i| { |
|
396 let (i, protocol) = terminated(u16_line, newline)(i)?; |
|
397 let (i, name) = terminated(a_line, newline)(i)?; |
|
398 let (i, pass) = a_line(i)?; |
|
399 Ok((i, Checker(protocol, name, pass))) |
|
400 }), |
|
401 preceded(pair(tag("CREATE_ROOM"), newline), |i| { |
|
402 let (i, name) = a_line(i)?; |
|
403 let (i, pass) = opt_arg(i)?; |
|
404 Ok((i, CreateRoom(name, pass))) |
|
405 }), |
|
406 preceded(pair(tag("JOIN_ROOM"), newline), |i| { |
|
407 let (i, name) = a_line(i)?; |
|
408 let (i, pass) = opt_arg(i)?; |
|
409 Ok((i, JoinRoom(name, pass))) |
|
410 }), |
|
411 preceded(pair(tag("ADD_TEAM"), newline), |i| { |
|
412 let (i, name) = terminated(a_line, newline)(i)?; |
|
413 let (i, color) = terminated(u8_line, newline)(i)?; |
|
414 let (i, grave) = terminated(a_line, newline)(i)?; |
|
415 let (i, fort) = terminated(a_line, newline)(i)?; |
|
416 let (i, voice_pack) = terminated(a_line, newline)(i)?; |
|
417 let (i, flag) = terminated(a_line, newline)(i)?; |
|
418 let (i, difficulty) = terminated(u8_line, newline)(i)?; |
|
419 let (i, hedgehogs) = hedgehog_array(i)?; |
|
420 Ok(( |
427 i, |
421 i, |
428 |i| terminatedc(i, hw_tag("PASSWORD"), newline), |
422 AddTeam(Box::new(TeamInfo { |
429 |i| { |
423 owner: String::new(), |
430 let (i, pass) = terminatedc(i, a_line, newline)?; |
424 name, |
431 let (i, salt) = a_line(i)?; |
425 color, |
432 Ok((i, Password(pass, salt))) |
426 grave, |
433 }, |
427 fort, |
434 ) |
428 voice_pack, |
435 }, |
429 flag, |
436 |i| { |
430 difficulty, |
437 precededc( |
431 hedgehogs, |
438 i, |
432 hedgehogs_number: 0, |
439 |i| terminatedc(i, hw_tag("CHECKER"), newline), |
433 })), |
440 |i| { |
434 )) |
441 let (i, protocol) = terminatedc(i, u16_line, newline)?; |
435 }), |
442 let (i, name) = terminatedc(i, a_line, newline)?; |
436 preceded(pair(tag("HH_NUM"), newline), |i| { |
443 let (i, pass) = a_line(i)?; |
437 let (i, name) = terminated(a_line, newline)(i)?; |
444 Ok((i, Checker(protocol, name, pass))) |
438 let (i, count) = u8_line(i)?; |
445 }, |
439 Ok((i, SetHedgehogsNumber(name, count))) |
446 ) |
440 }), |
447 }, |
441 preceded(pair(tag("TEAM_COLOR"), newline), |i| { |
448 |i| { |
442 let (i, name) = terminated(a_line, newline)(i)?; |
449 precededc( |
443 let (i, color) = u8_line(i)?; |
450 i, |
444 Ok((i, SetTeamColor(name, color))) |
451 |i| terminatedc(i, hw_tag("CREATE_ROOM"), newline), |
445 }), |
452 |i| { |
446 preceded(pair(tag("BAN"), newline), |i| { |
453 let (i, name) = a_line(i)?; |
447 let (i, n) = terminated(a_line, newline)(i)?; |
454 let (i, pass) = opt_arg(i)?; |
448 let (i, r) = terminated(a_line, newline)(i)?; |
455 Ok((i, CreateRoom(name, pass))) |
449 let (i, t) = u32_line(i)?; |
456 }, |
450 Ok((i, Ban(n, r, t))) |
457 ) |
451 }), |
458 }, |
452 preceded(pair(tag("BAN_IP"), newline), |i| { |
459 |i| { |
453 let (i, n) = terminated(a_line, newline)(i)?; |
460 precededc( |
454 let (i, r) = terminated(a_line, newline)(i)?; |
461 i, |
455 let (i, t) = u32_line(i)?; |
462 |i| terminatedc(i, hw_tag("JOIN_ROOM"), newline), |
456 Ok((i, BanIP(n, r, t))) |
463 |i| { |
457 }), |
464 let (i, name) = a_line(i)?; |
458 preceded(pair(tag("BAN_NICK"), newline), |i| { |
465 let (i, pass) = opt_arg(i)?; |
459 let (i, n) = terminated(a_line, newline)(i)?; |
466 Ok((i, JoinRoom(name, pass))) |
460 let (i, r) = terminated(a_line, newline)(i)?; |
467 }, |
461 let (i, t) = u32_line(i)?; |
468 ) |
462 Ok((i, BanNick(n, r, t))) |
469 }, |
463 }), |
470 |i| { |
|
471 precededc( |
|
472 i, |
|
473 |i| terminatedc(i, hw_tag("ADD_TEAM"), newline), |
|
474 |i| { |
|
475 let (i, name) = terminatedc(i, a_line, newline)?; |
|
476 let (i, color) = terminatedc(i, u8_line, newline)?; |
|
477 let (i, grave) = terminatedc(i, a_line, newline)?; |
|
478 let (i, fort) = terminatedc(i, a_line, newline)?; |
|
479 let (i, voice_pack) = terminatedc(i, a_line, newline)?; |
|
480 let (i, flag) = terminatedc(i, a_line, newline)?; |
|
481 let (i, difficulty) = terminatedc(i, u8_line, newline)?; |
|
482 let (i, hedgehogs) = hedgehog_array(i)?; |
|
483 Ok(( |
|
484 i, |
|
485 AddTeam(Box::new(TeamInfo { |
|
486 owner: String::new(), |
|
487 name, |
|
488 color, |
|
489 grave, |
|
490 fort, |
|
491 voice_pack, |
|
492 flag, |
|
493 difficulty, |
|
494 hedgehogs, |
|
495 hedgehogs_number: 0, |
|
496 })), |
|
497 )) |
|
498 }, |
|
499 ) |
|
500 }, |
|
501 |i| { |
|
502 precededc( |
|
503 i, |
|
504 |i| terminatedc(i, hw_tag("HH_NUM"), newline), |
|
505 |i| { |
|
506 let (i, name) = terminatedc(i, a_line, newline)?; |
|
507 let (i, count) = u8_line(i)?; |
|
508 Ok((i, SetHedgehogsNumber(name, count))) |
|
509 }, |
|
510 ) |
|
511 }, |
|
512 |i| { |
|
513 precededc( |
|
514 i, |
|
515 |i| terminatedc(i, hw_tag("TEAM_COLOR"), newline), |
|
516 |i| { |
|
517 let (i, name) = terminatedc(i, a_line, newline)?; |
|
518 let (i, color) = u8_line(i)?; |
|
519 Ok((i, SetTeamColor(name, color))) |
|
520 }, |
|
521 ) |
|
522 }, |
|
523 |i| { |
|
524 precededc( |
|
525 i, |
|
526 |i| terminatedc(i, hw_tag("BAN"), newline), |
|
527 |i| { |
|
528 let (i, n) = terminatedc(i, a_line, newline)?; |
|
529 let (i, r) = terminatedc(i, a_line, newline)?; |
|
530 let (i, t) = u32_line(i)?; |
|
531 Ok((i, Ban(n, r, t))) |
|
532 }, |
|
533 ) |
|
534 }, |
|
535 |i| { |
|
536 precededc( |
|
537 i, |
|
538 |i| terminatedc(i, hw_tag("BAN_IP"), newline), |
|
539 |i| { |
|
540 let (i, n) = terminatedc(i, a_line, newline)?; |
|
541 let (i, r) = terminatedc(i, a_line, newline)?; |
|
542 let (i, t) = u32_line(i)?; |
|
543 Ok((i, BanIP(n, r, t))) |
|
544 }, |
|
545 ) |
|
546 }, |
|
547 |i| { |
|
548 precededc( |
|
549 i, |
|
550 |i| terminatedc(i, hw_tag("BAN_NICK"), newline), |
|
551 |i| { |
|
552 let (i, n) = terminatedc(i, a_line, newline)?; |
|
553 let (i, r) = terminatedc(i, a_line, newline)?; |
|
554 let (i, t) = u32_line(i)?; |
|
555 Ok((i, BanNick(n, r, t))) |
|
556 }, |
|
557 ) |
|
558 }, |
|
559 ))(input) |
464 ))(input) |
560 } |
465 } |
561 |
466 |
562 pub fn malformed_message(input: &[u8]) -> HwResult<()> { |
467 pub fn malformed_message(input: &[u8]) -> HwResult<()> { |
563 let (i, _) = terminatedc(input, |i| take_until(&b"\n\n"[..])(i), end_of_message)?; |
468 map(terminated(take_until(&b"\n\n"[..]), end_of_message), |_| ())(input) |
564 Ok((i, ())) |
|
565 } |
469 } |
566 |
470 |
567 pub fn message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
471 pub fn message(input: &[u8]) -> HwResult<HwProtocolMessage> { |
568 precededc( |
472 preceded( |
569 input, |
473 take_while(|c| c == b'\n'), |
570 |i| take_while(|c| c == b'\n')(i), |
474 terminated( |
571 |i| { |
475 alt(( |
572 terminatedc( |
476 no_arg_message, |
573 i, |
477 single_arg_message, |
574 alt(( |
478 cmd_message, |
575 no_arg_message, |
479 config_message, |
576 single_arg_message, |
480 server_var_message, |
577 cmd_message, |
481 complex_message, |
578 config_message, |
482 )), |
579 server_var_message, |
483 end_of_message, |
580 complex_message, |
484 ), |
581 )), |
485 )(input) |
582 end_of_message, |
|
583 ) |
|
584 }, |
|
585 ) |
|
586 } |
486 } |
587 |
487 |
588 #[cfg(test)] |
488 #[cfg(test)] |
589 mod test { |
489 mod test { |
590 use super::message; |
490 use super::message; |