irc-proto: allow modes with no prefix

irc clients can query the details of some modes (e.g. request list of
banned users) by sending a message with no plus or minus prefix,
e.g. "MODE #chan b"
This commit is contained in:
Dominique Martinet 2023-06-23 21:27:47 +09:00
parent 8eef9c5688
commit 709151b94d

View file

@ -3,8 +3,6 @@ use std::fmt;
use crate::command::Command;
use crate::error::MessageParseError;
use crate::error::MessageParseError::InvalidModeString;
use crate::error::ModeParseError::*;
/// A marker trait for different kinds of Modes.
pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
@ -218,6 +216,8 @@ where
Plus(T, Option<String>),
/// Removing the specified mode, optionally with an argument.
Minus(T, Option<String>),
/// No prefix mode, used to query ban list on channel join.
NoPrefix(T),
}
impl<T> Mode<T>
@ -233,6 +233,11 @@ where
pub fn minus(inner: T, arg: Option<&str>) -> Mode<T> {
Mode::Minus(inner, arg.map(|s| s.to_owned()))
}
/// Create a no prefix mode with an `&str` argument.
pub fn no_prefix(inner: T) -> Mode<T> {
Mode::NoPrefix(inner)
}
}
impl<T> fmt::Display for Mode<T>
@ -245,6 +250,7 @@ where
Mode::Minus(ref mode, Some(ref arg)) => write!(f, "-{} {}", mode, arg),
Mode::Plus(ref mode, None) => write!(f, "+{}", mode),
Mode::Minus(ref mode, None) => write!(f, "-{}", mode),
Mode::NoPrefix(ref mode) => write!(f, "{}", mode),
}
}
}
@ -252,6 +258,7 @@ where
enum PlusMinus {
Plus,
Minus,
NoPrefix,
}
// MODE user [modes]
@ -287,11 +294,10 @@ where
let mut cur_mod = match modes.next() {
Some('+') => Plus,
Some('-') => Minus,
Some(c) => {
return Err(InvalidModeString {
string: pieces.join(" "),
cause: InvalidModeModifier { modifier: c },
})
Some(_) => {
// rewind modes
modes = first.chars();
NoPrefix
}
None => {
// No modifier
@ -314,6 +320,7 @@ where
res.push(match cur_mod {
Plus => Mode::Plus(mode, arg.map(|s| s.to_string())),
Minus => Mode::Minus(mode, arg.map(|s| s.to_string())),
NoPrefix => Mode::NoPrefix(mode),
})
}
}
@ -350,4 +357,13 @@ mod test {
let cmd = "MODE #foo".parse::<Message>().unwrap().command;
assert_eq!(Command::ChannelMODE("#foo".to_string(), vec![]), cmd);
}
#[test]
fn parse_no_plus() {
let cmd = "MODE #foo b".parse::<Message>().unwrap().command;
assert_eq!(
Command::ChannelMODE("#foo".to_string(), vec![Mode::NoPrefix(ChannelMode::Ban)]),
cmd
);
}
}