Merge pull request #199 from belak/fix-suffix-handling

Handle suffix as a plain param
This commit is contained in:
Aaron Weiss 2020-01-31 13:21:10 -05:00 committed by GitHub
commit bbc99b1d20
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 647 additions and 1322 deletions

View file

@ -29,7 +29,7 @@ async fn main() -> irc::error::Result<()> {
while let Some(message) = stream.next().await.transpose()? { while let Some(message) = stream.next().await.transpose()? {
match message.command { match message.command {
Command::Response(Response::RPL_ISUPPORT, _, _) => { Command::Response(Response::RPL_ISUPPORT, _) => {
client.send_privmsg( client.send_privmsg(
"#commits", "#commits",
format!( format!(

File diff suppressed because it is too large Load diff

View file

@ -36,7 +36,7 @@ impl Message {
/// # use irc_proto::Message; /// # use irc_proto::Message;
/// # fn main() { /// # fn main() {
/// let message = Message::new( /// let message = Message::new(
/// Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None /// Some("nickname!username@hostname"), "JOIN", vec!["#channel"]
/// ).unwrap(); /// ).unwrap();
/// # } /// # }
/// ``` /// ```
@ -44,9 +44,8 @@ impl Message {
prefix: Option<&str>, prefix: Option<&str>,
command: &str, command: &str,
args: Vec<&str>, args: Vec<&str>,
suffix: Option<&str>,
) -> Result<Message, MessageParseError> { ) -> Result<Message, MessageParseError> {
Message::with_tags(None, prefix, command, args, suffix) Message::with_tags(None, prefix, command, args)
} }
/// Creates a new IRCv3.2 message from the given components, including message tags. These tags /// Creates a new IRCv3.2 message from the given components, including message tags. These tags
@ -57,12 +56,11 @@ impl Message {
prefix: Option<&str>, prefix: Option<&str>,
command: &str, command: &str,
args: Vec<&str>, args: Vec<&str>,
suffix: Option<&str>,
) -> Result<Message, error::MessageParseError> { ) -> Result<Message, error::MessageParseError> {
Ok(Message { Ok(Message {
tags: tags, tags: tags,
prefix: prefix.map(|p| p.into()), prefix: prefix.map(|p| p.into()),
command: Command::new(command, args, suffix)?, command: Command::new(command, args)?,
}) })
} }
@ -74,7 +72,7 @@ impl Message {
/// # use irc_proto::Message; /// # use irc_proto::Message;
/// # fn main() { /// # fn main() {
/// let message = Message::new( /// let message = Message::new(
/// Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None /// Some("nickname!username@hostname"), "JOIN", vec!["#channel"]
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(message.source_nickname(), Some("nickname")); /// assert_eq!(message.source_nickname(), Some("nickname"));
/// # } /// # }
@ -98,11 +96,11 @@ impl Message {
/// # use irc_proto::Message; /// # use irc_proto::Message;
/// # fn main() { /// # fn main() {
/// let msg1 = Message::new( /// let msg1 = Message::new(
/// Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!") /// Some("ada"), "PRIVMSG", vec!["#channel", "Hi, everyone!"]
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(msg1.response_target(), Some("#channel")); /// assert_eq!(msg1.response_target(), Some("#channel"));
/// let msg2 = Message::new( /// let msg2 = Message::new(
/// Some("ada"), "PRIVMSG", vec!["betsy"], Some("betsy: hi") /// Some("ada"), "PRIVMSG", vec!["betsy", "betsy: hi"]
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(msg2.response_target(), Some("ada")); /// assert_eq!(msg2.response_target(), Some("ada"));
/// # } /// # }
@ -123,7 +121,7 @@ impl Message {
/// # use irc_proto::Message; /// # use irc_proto::Message;
/// # fn main() { /// # fn main() {
/// let msg = Message::new( /// let msg = Message::new(
/// Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!") /// Some("ada"), "PRIVMSG", vec!["#channel", "Hi, everyone!"]
/// ).unwrap(); /// ).unwrap();
/// assert_eq!(msg.to_string(), ":ada PRIVMSG #channel :Hi, everyone!\r\n"); /// assert_eq!(msg.to_string(), ":ada PRIVMSG #channel :Hi, everyone!\r\n");
/// # } /// # }
@ -243,9 +241,12 @@ impl FromStr for Message {
} }
}; };
let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect(); let mut args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
if let Some(suffix) = suffix {
args.push(suffix);
}
Message::with_tags(tags, prefix, command, args, suffix).map_err(|e| { Message::with_tags(tags, prefix, command, args).map_err(|e| {
ProtocolError::InvalidMessage { ProtocolError::InvalidMessage {
string: s.to_owned(), string: s.to_owned(),
cause: e, cause: e,
@ -286,7 +287,7 @@ mod test {
command: PRIVMSG(format!("test"), format!("Testing!")), command: PRIVMSG(format!("test"), format!("Testing!")),
}; };
assert_eq!( assert_eq!(
Message::new(None, "PRIVMSG", vec!["test"], Some("Testing!")).unwrap(), Message::new(None, "PRIVMSG", vec!["test", "Testing!"]).unwrap(),
message message
) )
} }
@ -294,56 +295,56 @@ mod test {
#[test] #[test]
fn source_nickname() { fn source_nickname() {
assert_eq!( assert_eq!(
Message::new(None, "PING", vec![], Some("data")) Message::new(None, "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
None None
); );
assert_eq!( assert_eq!(
Message::new(Some("irc.test.net"), "PING", vec![], Some("data")) Message::new(Some("irc.test.net"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
None None
); );
assert_eq!( assert_eq!(
Message::new(Some("test!test@test"), "PING", vec![], Some("data")) Message::new(Some("test!test@test"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
); );
assert_eq!( assert_eq!(
Message::new(Some("test@test"), "PING", vec![], Some("data")) Message::new(Some("test@test"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
); );
assert_eq!( assert_eq!(
Message::new(Some("test!test@irc.test.com"), "PING", vec![], Some("data")) Message::new(Some("test!test@irc.test.com"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
); );
assert_eq!( assert_eq!(
Message::new(Some("test!test@127.0.0.1"), "PING", vec![], Some("data")) Message::new(Some("test!test@127.0.0.1"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
); );
assert_eq!( assert_eq!(
Message::new(Some("test@test.com"), "PING", vec![], Some("data")) Message::new(Some("test@test.com"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
); );
assert_eq!( assert_eq!(
Message::new(Some("test"), "PING", vec![], Some("data")) Message::new(Some("test"), "PING", vec!["data"])
.unwrap() .unwrap()
.source_nickname(), .source_nickname(),
Some("test") Some("test")
@ -357,7 +358,7 @@ mod test {
prefix: None, prefix: None,
command: PRIVMSG(format!("test"), format!("Testing!")), command: PRIVMSG(format!("test"), format!("Testing!")),
}; };
assert_eq!(&message.to_string()[..], "PRIVMSG test :Testing!\r\n"); assert_eq!(&message.to_string()[..], "PRIVMSG test Testing!\r\n");
let message = Message { let message = Message {
tags: None, tags: None,
prefix: Some("test!test@test".into()), prefix: Some("test!test@test".into()),
@ -465,8 +466,7 @@ mod test {
prefix: Some("test!test@test".into()), prefix: Some("test!test@test".into()),
command: Raw( command: Raw(
format!("COMMAND"), format!("COMMAND"),
vec![format!("ARG:test")], vec![format!("ARG:test"), format!("Testing!")],
Some(format!("Testing!")),
), ),
}; };
let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into(); let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into();

View file

@ -13,6 +13,9 @@ pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
/// Returns true if this mode takes an argument, and false otherwise. /// Returns true if this mode takes an argument, and false otherwise.
fn takes_arg(&self) -> bool; fn takes_arg(&self) -> bool;
/// Creates a Mode from a given char.
fn from_char(c: char) -> Self;
} }
/// User modes for the MODE command. /// User modes for the MODE command.
@ -47,9 +50,7 @@ impl ModeType for UserMode {
fn takes_arg(&self) -> bool { fn takes_arg(&self) -> bool {
false false
} }
}
impl UserMode {
fn from_char(c: char) -> UserMode { fn from_char(c: char) -> UserMode {
use self::UserMode::*; use self::UserMode::*;
@ -144,9 +145,7 @@ impl ModeType for ChannelMode {
_ => false, _ => false,
} }
} }
}
impl ChannelMode {
fn from_char(c: char) -> ChannelMode { fn from_char(c: char) -> ChannelMode {
use self::ChannelMode::*; use self::ChannelMode::*;
@ -252,49 +251,8 @@ enum PlusMinus {
impl Mode<UserMode> { impl Mode<UserMode> {
// TODO: turning more edge cases into errors. // TODO: turning more edge cases into errors.
/// Parses the specified mode string as user modes. /// Parses the specified mode string as user modes.
pub fn as_user_modes(s: &str) -> Result<Vec<Mode<UserMode>>, MessageParseError> { pub fn as_user_modes(pieces: &[&str]) -> Result<Vec<Mode<UserMode>>, MessageParseError> {
use self::PlusMinus::*; parse_modes(pieces)
let mut res = vec![];
let mut pieces = s.split(' ');
for term in pieces.clone() {
if term.starts_with('+') || term.starts_with('-') {
let _ = pieces.next();
let mut chars = term.chars();
let init = match chars.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: InvalidModeModifier { modifier: c },
})
}
None => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: MissingModeModifier,
})
}
};
for c in chars {
let mode = UserMode::from_char(c);
let arg = if mode.takes_arg() {
pieces.next()
} else {
None
};
res.push(match init {
Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
})
}
}
}
Ok(res)
} }
} }
@ -302,48 +260,67 @@ impl Mode<UserMode> {
impl Mode<ChannelMode> { impl Mode<ChannelMode> {
// TODO: turning more edge cases into errors. // TODO: turning more edge cases into errors.
/// Parses the specified mode string as channel modes. /// Parses the specified mode string as channel modes.
pub fn as_channel_modes(s: &str) -> Result<Vec<Mode<ChannelMode>>, MessageParseError> { pub fn as_channel_modes(pieces: &[&str]) -> Result<Vec<Mode<ChannelMode>>, MessageParseError> {
use self::PlusMinus::*; parse_modes(pieces)
}
}
let mut res = vec![]; fn parse_modes<T>(pieces: &[&str]) -> Result<Vec<Mode<T>>, MessageParseError>
let mut pieces = s.split(' '); where
for term in pieces.clone() { T: ModeType,
if term.starts_with('+') || term.starts_with('-') { {
let _ = pieces.next(); use self::PlusMinus::*;
let mut chars = term.chars(); let mut res = vec![];
let init = match chars.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: InvalidModeModifier { modifier: c },
})
}
None => {
return Err(InvalidModeString {
string: s.to_owned(),
cause: MissingModeModifier,
})
}
};
for c in chars { if let Some((first, rest)) = pieces.split_first() {
let mode = ChannelMode::from_char(c); let mut modes = first.chars();
let mut args = rest.iter();
let mut cur_mod = match modes.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: pieces.join(" ").to_owned(),
cause: InvalidModeModifier { modifier: c },
})
}
None => {
return Err(InvalidModeString {
string: pieces.join(" ").to_owned(),
cause: MissingModeModifier,
})
}
};
for c in modes {
match c {
'+' => cur_mod = Plus,
'-' => cur_mod = Minus,
_ => {
let mode = T::from_char(c);
let arg = if mode.takes_arg() { let arg = if mode.takes_arg() {
pieces.next() // TODO: if there's no arg, this should error
args.next()
} else { } else {
None None
}; };
res.push(match init { res.push(match cur_mod {
Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())), Plus => Mode::Plus(mode, arg.map(|s| s.to_string())),
Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())), Minus => Mode::Minus(mode, arg.map(|s| s.to_string())),
}) })
} }
} }
} }
// TODO: if there are extra args left, this should error
Ok(res) Ok(res)
} else {
Err(InvalidModeString {
string: pieces.join(" ").to_owned(),
cause: MissingModeModifier,
})
} }
} }

View file

@ -570,11 +570,9 @@ impl ClientState {
} }
} }
} }
Command::Response(Response::RPL_NAMREPLY, ref args, ref suffix) => { Command::Response(Response::RPL_NAMREPLY, ref args) => self.handle_namreply(args),
self.handle_namreply(args, suffix) Command::Response(Response::RPL_ENDOFMOTD, _)
} | Command::Response(Response::ERR_NOMOTD, _) => {
Command::Response(Response::RPL_ENDOFMOTD, _, _)
| Command::Response(Response::ERR_NOMOTD, _, _) => {
self.send_nick_password()?; self.send_nick_password()?;
self.send_umodes()?; self.send_umodes()?;
@ -593,8 +591,8 @@ impl ClientState {
self.send_join(chan)? self.send_join(chan)?
} }
} }
Command::Response(Response::ERR_NICKNAMEINUSE, _, _) Command::Response(Response::ERR_NICKNAMEINUSE, _)
| Command::Response(Response::ERR_ERRONEOUSNICKNAME, _, _) => { | Command::Response(Response::ERR_ERRONEOUSNICKNAME, _) => {
let alt_nicks = self.config().alternate_nicknames(); let alt_nicks = self.config().alternate_nicknames();
let mut index = self.alt_nick_index.write(); let mut index = self.alt_nick_index.write();
@ -623,20 +621,19 @@ impl ClientState {
}; };
for s in seq { for s in seq {
self.send(NICKSERV(format!( self.send(NICKSERV(vec!(
"{} {} {}", s.to_string(),
s, self.config().nickname()?.to_string(),
self.config().nickname()?, self.config().nick_password().to_string(),
self.config().nick_password()
)))?; )))?;
} }
*index = 0; *index = 0;
self.send(NICK(self.config().nickname()?.to_owned()))? self.send(NICK(self.config().nickname()?.to_owned()))?
} }
self.send(NICKSERV(format!( self.send(NICKSERV(vec!(
"IDENTIFY {}", "IDENTIFY".to_string(),
self.config().nick_password() self.config().nick_password().to_string()
))) )))
} }
} }
@ -647,15 +644,20 @@ impl ClientState {
} else { } else {
self.send_mode( self.send_mode(
self.current_nickname(), self.current_nickname(),
&Mode::as_user_modes(self.config().umodes()).map_err(|e| { &Mode::as_user_modes(
error::Error::InvalidMessage { self.config()
string: format!( .umodes()
"MODE {} {}", .split(' ')
self.current_nickname(), .collect::<Vec<_>>()
self.config().umodes() .as_ref(),
), )
cause: e, .map_err(|e| error::Error::InvalidMessage {
} string: format!(
"MODE {} {}",
self.current_nickname(),
self.config().umodes()
),
cause: e,
})?, })?,
) )
} }
@ -740,20 +742,18 @@ impl ClientState {
} }
#[cfg(feature = "nochanlists")] #[cfg(feature = "nochanlists")]
fn handle_namreply(&self, _: &[String], _: &Option<String>) {} fn handle_namreply(&self, _: &[String]) {}
#[cfg(not(feature = "nochanlists"))] #[cfg(not(feature = "nochanlists"))]
fn handle_namreply(&self, args: &[String], suffix: &Option<String>) { fn handle_namreply(&self, args: &[String]) {
if let Some(ref users) = *suffix { if args.len() == 4 {
if args.len() == 3 { let chan = &args[2];
let chan = &args[2]; for user in args[3].split(' ') {
for user in users.split(' ') { self.chanlists
self.chanlists .write()
.write() .entry(chan.clone())
.entry(chan.clone()) .or_insert_with(Vec::new)
.or_insert_with(Vec::new) .push(User::new(user))
.push(User::new(user))
}
} }
} }
} }
@ -1219,8 +1219,8 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"NICK :test2\r\nNICKSERV GHOST test password\r\n\ "NICK test2\r\nNICKSERV GHOST test password\r\n\
NICK :test\r\nNICKSERV IDENTIFY password\r\nJOIN #test\r\nJOIN #test2\r\n" NICK test\r\nNICKSERV IDENTIFY password\r\nJOIN #test\r\nJOIN #test2\r\n"
); );
Ok(()) Ok(())
} }
@ -1243,8 +1243,8 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"NICK :test2\r\nNICKSERV RECOVER test password\ "NICK test2\r\nNICKSERV RECOVER test password\
\r\nNICKSERV RELEASE test password\r\nNICK :test\r\nNICKSERV IDENTIFY password\ \r\nNICKSERV RELEASE test password\r\nNICK test\r\nNICKSERV IDENTIFY password\
\r\nJOIN #test\r\nJOIN #test2\r\n" \r\nJOIN #test\r\nJOIN #test2\r\n"
); );
Ok(()) Ok(())
@ -1278,7 +1278,7 @@ mod test {
}) })
.await?; .await?;
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!(&get_client_value(client)[..], "NICK :test2\r\n"); assert_eq!(&get_client_value(client)[..], "NICK test2\r\n");
Ok(()) Ok(())
} }
@ -1332,10 +1332,10 @@ mod test {
async fn send_raw_is_really_raw() -> Result<()> { async fn send_raw_is_really_raw() -> Result<()> {
let mut client = Client::from_config(test_config()).await?; let mut client = Client::from_config(test_config()).await?;
assert!(client assert!(client
.send(Raw("PASS".to_owned(), vec!["password".to_owned()], None)) .send(Raw("PASS".to_owned(), vec!["password".to_owned()]))
.is_ok()); .is_ok());
assert!(client assert!(client
.send(Raw("NICK".to_owned(), vec!["test".to_owned()], None)) .send(Raw("NICK".to_owned(), vec!["test".to_owned()]))
.is_ok()); .is_ok());
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
@ -1622,7 +1622,7 @@ mod test {
#[tokio::test] #[tokio::test]
#[cfg(feature = "ctcp")] #[cfg(feature = "ctcp")]
async fn ctcp_ping_no_timestamp() -> Result<()> { async fn ctcp_ping_no_timestamp() -> Result<()> {
let value = ":test!test@test PRIVMSG test :\u{001}PING\u{001}\r\n"; let value = ":test!test@test PRIVMSG test \u{001}PING\u{001}\r\n";
let mut client = Client::from_config(Config { let mut client = Client::from_config(Config {
mock_initial_value: Some(value.to_owned()), mock_initial_value: Some(value.to_owned()),
..test_config() ..test_config()
@ -1640,8 +1640,8 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"CAP END\r\nNICK :test\r\n\ "CAP END\r\nNICK test\r\n\
USER test 0 * :test\r\n" USER test 0 * test\r\n"
); );
Ok(()) Ok(())
} }
@ -1658,8 +1658,8 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"CAP END\r\nPASS :password\r\nNICK :test\r\n\ "CAP END\r\nPASS password\r\nNICK test\r\n\
USER test 0 * :test\r\n" USER test 0 * test\r\n"
); );
Ok(()) Ok(())
} }
@ -1669,7 +1669,7 @@ mod test {
let mut client = Client::from_config(test_config()).await?; let mut client = Client::from_config(test_config()).await?;
client.send_pong("irc.test.net")?; client.send_pong("irc.test.net")?;
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!(&get_client_value(client)[..], "PONG :irc.test.net\r\n"); assert_eq!(&get_client_value(client)[..], "PONG irc.test.net\r\n");
Ok(()) Ok(())
} }
@ -1699,7 +1699,7 @@ mod test {
let mut client = Client::from_config(test_config()).await?; let mut client = Client::from_config(test_config()).await?;
client.send_oper("test", "test")?; client.send_oper("test", "test")?;
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!(&get_client_value(client)[..], "OPER test :test\r\n"); assert_eq!(&get_client_value(client)[..], "OPER test test\r\n");
Ok(()) Ok(())
} }
@ -1846,7 +1846,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}MESSAGE\u{001}\r\n" "PRIVMSG test \u{001}MESSAGE\u{001}\r\n"
); );
Ok(()) Ok(())
} }
@ -1872,7 +1872,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}FINGER\u{001}\r\n" "PRIVMSG test \u{001}FINGER\u{001}\r\n"
); );
Ok(()) Ok(())
} }
@ -1885,7 +1885,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}VERSION\u{001}\r\n" "PRIVMSG test \u{001}VERSION\u{001}\r\n"
); );
Ok(()) Ok(())
} }
@ -1898,7 +1898,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}SOURCE\u{001}\r\n" "PRIVMSG test \u{001}SOURCE\u{001}\r\n"
); );
Ok(()) Ok(())
} }
@ -1911,7 +1911,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}USERINFO\u{001}\r\n" "PRIVMSG test \u{001}USERINFO\u{001}\r\n"
); );
Ok(()) Ok(())
} }
@ -1937,7 +1937,7 @@ mod test {
client.stream()?.collect().await?; client.stream()?.collect().await?;
assert_eq!( assert_eq!(
&get_client_value(client)[..], &get_client_value(client)[..],
"PRIVMSG test :\u{001}TIME\u{001}\r\n" "PRIVMSG test \u{001}TIME\u{001}\r\n"
); );
Ok(()) Ok(())
} }