1use std::fmt;
3
4use crate::proto::Command;
5use crate::proto::MessageParseError::{self, *};
6use crate::proto::ModeParseError::*;
7
8pub trait ModeType: fmt::Display + fmt::Debug + Clone + PartialEq {
10 fn mode(target: &str, modes: &[Mode<Self>]) -> Command;
12
13 fn takes_arg(&self) -> bool;
15}
16
17#[derive(Clone, Debug, PartialEq)]
19pub enum UserMode {
20 Away,
22 Invisible,
24 Wallops,
26 Restricted,
28 Oper,
30 LocalOper,
32 ServerNotices,
34 MaskedHost,
36
37 Unknown(char),
39}
40
41impl ModeType for UserMode {
42 fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
43 Command::UserMODE(target.to_owned(), modes.to_owned())
44 }
45
46 fn takes_arg(&self) -> bool {
47 false
48 }
49}
50
51impl UserMode {
52 fn from_char(c: char) -> UserMode {
53 use self::UserMode::*;
54
55 match c {
56 'a' => Away,
57 'i' => Invisible,
58 'w' => Wallops,
59 'r' => Restricted,
60 'o' => Oper,
61 'O' => LocalOper,
62 's' => ServerNotices,
63 'x' => MaskedHost,
64 _ => Unknown(c),
65 }
66 }
67}
68
69impl fmt::Display for UserMode {
70 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
71 use self::UserMode::*;
72
73 write!(
74 f,
75 "{}",
76 match *self {
77 Away => 'a',
78 Invisible => 'i',
79 Wallops => 'w',
80 Restricted => 'r',
81 Oper => 'o',
82 LocalOper => 'O',
83 ServerNotices => 's',
84 MaskedHost => 'x',
85 Unknown(c) => c,
86 }
87 )
88 }
89}
90
91#[derive(Clone, Debug, PartialEq)]
93pub enum ChannelMode {
94 Ban,
96 Exception,
98 Limit,
100 InviteOnly,
102 InviteException,
104 Key,
106 Moderated,
108 RegisteredOnly,
110 Secret,
112 ProtectedTopic,
114 NoExternalMessages,
116
117 Founder,
119 Admin,
121 Oper,
123 Halfop,
125 Voice,
127
128 Unknown(char),
130}
131
132impl ModeType for ChannelMode {
133 fn mode(target: &str, modes: &[Mode<Self>]) -> Command {
134 Command::ChannelMODE(target.to_owned(), modes.to_owned())
135 }
136
137 fn takes_arg(&self) -> bool {
138 use self::ChannelMode::*;
139
140 match *self {
141 Ban | Exception | Limit | InviteException | Key | Founder | Admin | Oper | Halfop
142 | Voice => true,
143 _ => false,
144 }
145 }
146}
147
148impl ChannelMode {
149 fn from_char(c: char) -> ChannelMode {
150 use self::ChannelMode::*;
151
152 match c {
153 'b' => Ban,
154 'e' => Exception,
155 'l' => Limit,
156 'i' => InviteOnly,
157 'I' => InviteException,
158 'k' => Key,
159 'm' => Moderated,
160 'r' => RegisteredOnly,
161 's' => Secret,
162 't' => ProtectedTopic,
163 'n' => NoExternalMessages,
164 'q' => Founder,
165 'a' => Admin,
166 'o' => Oper,
167 'h' => Halfop,
168 'v' => Voice,
169 _ => Unknown(c),
170 }
171 }
172}
173
174impl fmt::Display for ChannelMode {
175 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
176 use self::ChannelMode::*;
177
178 write!(
179 f,
180 "{}",
181 match *self {
182 Ban => 'b',
183 Exception => 'e',
184 Limit => 'l',
185 InviteOnly => 'i',
186 InviteException => 'I',
187 Key => 'k',
188 Moderated => 'm',
189 RegisteredOnly => 'r',
190 Secret => 's',
191 ProtectedTopic => 't',
192 NoExternalMessages => 'n',
193 Founder => 'q',
194 Admin => 'a',
195 Oper => 'o',
196 Halfop => 'h',
197 Voice => 'v',
198 Unknown(c) => c,
199 }
200 )
201 }
202}
203
204#[derive(Clone, Debug, PartialEq)]
206pub enum Mode<T>
207where
208 T: ModeType,
209{
210 Plus(T, Option<String>),
212 Minus(T, Option<String>),
214}
215
216impl<T> Mode<T>
217where
218 T: ModeType,
219{
220 pub fn plus(inner: T, arg: Option<&str>) -> Mode<T> {
222 Mode::Plus(inner, arg.map(|s| s.to_owned()))
223 }
224
225 pub fn minus(inner: T, arg: Option<&str>) -> Mode<T> {
227 Mode::Minus(inner, arg.map(|s| s.to_owned()))
228 }
229}
230
231impl<T> fmt::Display for Mode<T>
232where
233 T: ModeType,
234{
235 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
236 match *self {
237 Mode::Plus(ref mode, Some(ref arg)) => write!(f, "+{} {}", mode, arg),
238 Mode::Minus(ref mode, Some(ref arg)) => write!(f, "+{} {}", mode, arg),
239 Mode::Plus(ref mode, None) => write!(f, "+{}", mode),
240 Mode::Minus(ref mode, None) => write!(f, "-{}", mode),
241 }
242 }
243}
244
245enum PlusMinus {
246 Plus,
247 Minus,
248}
249
250impl Mode<UserMode> {
252 pub fn from_user_mode_string(s: &str) -> Result<Vec<Mode<UserMode>>, MessageParseError> {
255 use self::PlusMinus::*;
256
257 let mut res = vec![];
258 let mut pieces = s.split(' ');
259 for term in pieces.clone() {
260 if term.starts_with('+') || term.starts_with('-') {
261 let _ = pieces.next();
262
263 let mut chars = term.chars();
264 let init = match chars.next() {
265 Some('+') => Plus,
266 Some('-') => Minus,
267 Some(c) => {
268 return Err(InvalidModeString {
269 string: s.to_owned(),
270 cause: InvalidModeModifier { modifier: c },
271 })
272 }
273 None => {
274 return Err(InvalidModeString {
275 string: s.to_owned(),
276 cause: MissingModeModifier,
277 })
278 }
279 };
280
281 for c in chars {
282 let mode = UserMode::from_char(c);
283 let arg = if mode.takes_arg() {
284 pieces.next()
285 } else {
286 None
287 };
288 res.push(match init {
289 Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
290 Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
291 })
292 }
293 }
294 }
295
296 Ok(res)
297 }
298}
299
300impl Mode<ChannelMode> {
302 pub fn from_channel_mode_string(s: &str) -> Result<Vec<Mode<ChannelMode>>, MessageParseError> {
305 use self::PlusMinus::*;
306
307 let mut res = vec![];
308 let mut pieces = s.split(' ');
309 for term in pieces.clone() {
310 if term.starts_with('+') || term.starts_with('-') {
311 let _ = pieces.next();
312
313 let mut chars = term.chars();
314 let init = match chars.next() {
315 Some('+') => Plus,
316 Some('-') => Minus,
317 Some(c) => {
318 return Err(InvalidModeString {
319 string: s.to_owned(),
320 cause: InvalidModeModifier { modifier: c },
321 })
322 }
323 None => {
324 return Err(InvalidModeString {
325 string: s.to_owned(),
326 cause: MissingModeModifier,
327 })
328 }
329 };
330
331 for c in chars {
332 let mode = ChannelMode::from_char(c);
333 let arg = if mode.takes_arg() {
334 pieces.next()
335 } else {
336 None
337 };
338 res.push(match init {
339 Plus => Mode::Plus(mode, arg.map(|s| s.to_owned())),
340 Minus => Mode::Minus(mode, arg.map(|s| s.to_owned())),
341 })
342 }
343 }
344 }
345
346 Ok(res)
347 }
348}