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