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