1use std::fmt::{Debug, Display, Error as FmtError, Formatter};
2use std::result;
3use std::error;
4
5use super::escaping::{escape, unescape};
6
7pub const IRC_TRAILING: char = ':';
8pub const IRC_TAG_START: char = '@';
9pub const IRC_PREFIX_START: char = ':';
10pub const IRC_TAG_VALUE_SEP: char = '=';
11pub const IRC_TAG_VENDOR_SEP: char = '/';
12pub const IRC_TAG_END_SEP: char = ';';
13pub const IRC_PREFIX_USER_SEP: char = '!';
14pub const IRC_PREFIX_HOST_SEP: char = '@';
15
16pub type Result<T> = result::Result<T, ParseError>;
17
18fn check_valid_key(ch: char) -> bool { ch.is_alphabetic() || ch.is_digit(10) || ch == '-' }
20
21fn get_char_at(s: &str, ind: usize) -> char {
23 s[ind..].chars().next().unwrap()
24}
25
26pub struct ParseError {
27 message: &'static str,
28 kind: ParseErrorKind,
29}
30
31impl ParseError {
32 fn new_unexpected(msg: &'static str, ch: char) -> ParseError {
33 ParseError {
34 message: msg,
35 kind: ParseErrorKind::Unexpected(ch)
36 }
37 }
38
39 fn new_bad_syntax(msg: &'static str) -> ParseError {
40 ParseError {
41 message: msg,
42 kind: ParseErrorKind::BadSyntax
43 }
44 }
45
46 fn new_missing_command(msg: &'static str) -> ParseError {
47 ParseError {
48 message: msg,
49 kind: ParseErrorKind::BadSyntax,
50 }
51 }
52}
53
54impl error::Error for ParseError {
55
56}
57
58impl Display for ParseError {
59 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
60 f.write_str(self.message)?;
61 f.write_str("; ")?;
62
63 match &self.kind {
64 ParseErrorKind::Unexpected(ch) => write!(f, "unexpected char '{}'", ch),
65 ParseErrorKind::BadSyntax => f.write_str("bad syntax"),
66 ParseErrorKind::MissingCommand => f.write_str("missing command"),
67 }
68 }
69}
70
71impl Debug for ParseError {
72 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
73 <ParseError as Display>::fmt(self, f)
74 }
75}
76
77pub enum ParseErrorKind {
78 Unexpected(char),
79 BadSyntax,
80 MissingCommand,
81}
82
83#[derive(PartialEq)]
88pub struct Tag {
89 key: String,
90 vendor: Option<String>,
91 value: Option<String>,
92}
93
94impl Tag {
95 pub fn new(key: &str, value: Option<&str>) -> Tag {
96 Tag::new_with_vendor(key, value, None)
97 }
98
99 pub fn new_with_vendor(key: &str, value: Option<&str>, vendor: Option<&str>) -> Tag {
100 Tag {
101 key: String::from(key),
102 vendor: match vendor {
103 Some(s) => Some(String::from(s)),
104 None => None,
105 },
106 value: match value {
107 Some(s) => Some(String::from(s)),
108 None => None,
109 }
110 }
111 }
112
113 pub fn key(&self) -> &str {
114 &self.key
115 }
116
117 pub fn vendor(&self) -> Option<&str> {
118 match &self.vendor {
119 Some(s) => Some(s),
120 None => None,
121 }
122 }
123
124 pub fn value(&self) -> Option<&str> {
125 match &self.value {
126 Some(s) => Some(s),
127 None => None,
128 }
129 }
130
131 pub fn set_key(&mut self, key: &str) {
132 self.key = String::from(key);
133 }
134
135 pub fn set_vendor(&mut self, vendor: &str) {
136 self.vendor = Some(String::from(vendor));
137 }
138
139 pub fn set_value(&mut self, value: &str) {
140 self.value = Some(String::from(value));
141 }
142}
143
144impl Display for Tag {
145 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
146 if let Some(ven) = &self.vendor {
148 f.write_str(ven)?;
149 f.write_str("/")?;
150 }
151
152 f.write_str(&self.key)?;
153
154 if let Some(value) = &self.value {
155 f.write_str("=")?;
156 f.write_str(&escape(value))?;
157 }
158
159 Ok(())
160 }
161}
162
163impl Debug for Tag {
164 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
165 f.write_str(&self.key)?;
166 if let Some(val) = &self.value { write!(f, "=\"{}\"", val)?; }
167 if let Some(ven) = &self.vendor { write!(f, " ({})", ven)?; }
168
169 Ok(())
170 }
171}
172
173#[derive(PartialEq)]
175pub struct Prefix {
176 origin: String,
177 user: Option<String>,
178 host: Option<String>,
179}
180
181impl Prefix {
182 pub fn new(origin: &str, user: Option<&str>, host: Option<&str>) -> Prefix {
183 Prefix {
184 origin: String::from(origin),
185 user: match user {
186 Some(s) => Some(String::from(s)),
187 None => None,
188 },
189 host: match host {
190 Some(s) => Some(String::from(s)),
191 None => None,
192 }
193 }
194 }
195
196 pub fn parse(pre: &str) -> Prefix {
197 Prefix {
198 origin: match pre.find(IRC_PREFIX_USER_SEP) {
199 Some(i) => String::from(&pre[..i]),
200 None => match pre.find(IRC_PREFIX_HOST_SEP) {
201 Some(i) => String::from(&pre[..i]),
202 None => String::from(pre),
203 }
204 },
205 user: match pre.find(IRC_PREFIX_USER_SEP) {
206 Some(i) => match pre.find(IRC_PREFIX_HOST_SEP) {
207 Some(j) => Some(String::from(&pre[(i+IRC_PREFIX_USER_SEP.len_utf8())..j])),
208 None => Some(String::from(&pre[(i+IRC_PREFIX_USER_SEP.len_utf8())..])),
209 },
210 None => None,
211 },
212 host: match pre.find(IRC_PREFIX_HOST_SEP) {
213 Some(i) => Some(String::from(&pre[(i+IRC_PREFIX_HOST_SEP.len_utf8())..])),
214 None => None,
215 }
216 }
217 }
218
219 pub fn origin(&self) -> &str {
220 &self.origin
221 }
222
223 pub fn user(&self) -> Option<&str> {
224 match &self.user {
225 Some(s) => Some(s),
226 None => None,
227 }
228 }
229
230 pub fn host(&self) -> Option<&str> {
231 match &self.host {
232 Some(s) => Some(s),
233 None => None,
234 }
235 }
236
237 pub fn set_origin(&mut self, s: &str) {
238 self.origin = String::from(s);
239 }
240
241 pub fn set_user(&mut self, s: &str) {
242 self.user = Some(String::from(s));
243 }
244
245 pub fn set_host(&mut self, s: &str) {
246 self.host = Some(String::from(s));
247 }
248}
249
250impl Display for Prefix {
251 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
252 f.write_str(&self.origin)?;
253
254 if let Some(user) = &self.user {
255 f.write_str("!")?;
256 f.write_str(user)?;
257 }
258
259 if let Some(host) = &self.host {
260 f.write_str("@")?;
261 f.write_str(host)?;
262 }
263
264 Ok(())
265 }
266}
267
268impl Debug for Prefix {
269 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
270 f.write_str("{")?;
271 write!(f, "origin=\"{}\"", self.origin)?;
272 if let Some(user) = &self.user {
273 write!(f, ", user=\"{}\"", user)?;
274 }
275 if let Some(host) = &self.host {
276 write!(f, ", host=\"{}\"", host)?;
277 }
278 f.write_str("}")
279 }
280}
281
282pub struct TagsIter<'a> {
284 cursor: usize,
285 inner: &'a str,
286}
287
288impl<'a> TagsIter<'a> {
289 pub fn new(tags: &'a str) -> TagsIter<'a> {
290 TagsIter {
291 cursor: 0,
292 inner: tags,
293 }
294 }
295}
296
297impl<'a> Iterator for TagsIter<'a> {
298 type Item = Result<Tag>;
299
300 fn next(&mut self) -> Option<Self::Item> {
301 if self.cursor >= self.inner.len() {
302 return None
303 }
304
305 let mut vendor: Option<String> = None;
307
308 let mut has_value = false;
310
311 let mut start = self.cursor;
313 while self.cursor < self.inner.len() {
314 let ch = get_char_at(self.inner, self.cursor);
315 if ch == IRC_TAG_VENDOR_SEP {
317 if vendor.is_none() {
319 vendor = Some(String::from(&self.inner[start..self.cursor]));
320 start = self.cursor + ch.len_utf8();
321 } else {
322 return Some(Err(ParseError::new_bad_syntax(
323 "tags may only have one vendor")));
324 }
325
326 self.cursor += ch.len_utf8();
327 continue;
328 }
329
330 if ch == IRC_TAG_VALUE_SEP {
332 has_value = true;
333 break;
334 }
335
336 if ch == IRC_TAG_END_SEP {
338 break;
339 }
340
341 if !check_valid_key(ch) {
343 return Some(Err(ParseError::new_unexpected(
344 "tags can only contain letters, digits or hyphens", ch)));
345 }
346
347 self.cursor += ch.len_utf8();
348 }
349
350 let tag = Tag{
352 key: String::from(&self.inner[start..self.cursor]),
353 vendor: vendor,
354 value: if has_value {
355 self.cursor += IRC_TAG_VALUE_SEP.len_utf8();
357
358 let start = self.cursor;
360 while self.cursor < self.inner.len() {
361 let ch = get_char_at(self.inner, self.cursor);
362
363 if ch == IRC_TAG_END_SEP {
364 break;
365 }
366
367 self.cursor += ch.len_utf8();
368 }
369
370 Some(unescape(&self.inner[start..self.cursor]))
372 } else {
374 None
375 },
376 };
377
378 self.cursor += IRC_TAG_END_SEP.len_utf8();
380
381 Some(Ok(tag))
382 }
383}
384
385pub struct ParamsIter<'a> {
387 cursor: usize,
388 inner: &'a str,
389}
390
391impl<'a> ParamsIter<'a> {
392 pub fn new(params: &'a str) -> ParamsIter<'a> {
393 ParamsIter {
394 cursor: 0,
395 inner: params,
396 }
397 }
398}
399
400impl<'a> Iterator for ParamsIter<'a> {
401 type Item = &'a str;
402
403 fn next(&mut self) -> Option<Self::Item> {
404 if self.cursor >= self.inner.len() {
405 return None;
406 }
407
408 let mut trailing = false;
410
411 let mut start = self.cursor;
412 let mut offset: usize = 0;
413 while self.cursor < self.inner.len() {
414 let ch = get_char_at(self.inner, self.cursor);
415 if ch.is_whitespace() && !trailing {
416 offset += ch.len_utf8();
417 while self.cursor+offset < self.inner.len() {
419 let ch = get_char_at(self.inner, self.cursor+offset);
420 if !ch.is_whitespace() {
421 break;
422 }
423
424 offset += ch.len_utf8();
425 }
426
427 break;
428 }
429
430 if start == self.cursor {
431 if ch == IRC_TRAILING {
433 trailing = true;
434 start += ch.len_utf8();
435 }
436 }
437
438 self.cursor += ch.len_utf8();
439 }
440
441 let slice = &self.inner[start..self.cursor];
443 self.cursor += offset;
444 Some(slice)
445 }
446}
447
448#[derive(Debug, PartialEq)]
472pub struct Message {
473 tags: Vec<Tag>,
474 prefix: Option<Prefix>,
475 command: String,
476 params: Vec<String>,
477}
478
479impl Message {
480 pub fn new(command: &str) -> Message {
481 Message {
482 tags: Vec::new(),
483 prefix: None,
484 command: String::from(command),
485 params: Vec::new(),
486 }
487 }
488
489 pub fn parse<S: AsRef<str> + ?Sized>(s: &S) -> Result<Message> {
490 let mut line = s.as_ref();
491
492 if line.is_empty() {
493 return Err(ParseError::new_missing_command("missing irc command!"))
494 }
495
496 Ok(Message {
497 tags: if line.starts_with(IRC_TAG_START) {
499 let mut split = line.splitn(2, char::is_whitespace);
501
502 let tags = split.next().unwrap();
503 line = match split.next() {
504 Some(line) => line,
505 None => return Err(ParseError::new_missing_command("missing irc command!")),
506 };
507
508 TagsIter::new(&tags[IRC_TAG_START.len_utf8()..]).filter_map(|r| match r {
509 Ok(t) => Some(t),
510 Err(_) => None,
511 }).collect()
512 } else {
513 Vec::new()
514 },
515 prefix: if line.starts_with(IRC_PREFIX_START) {
517 let mut split = line.splitn(2, char::is_whitespace);
519
520 let prefix = split.next().unwrap();
521 line = match split.next() {
522 Some(line) => line,
523 None => return Err(ParseError::new_missing_command("missing irc command!")),
524 };
525
526 Some(Prefix::parse(&prefix[IRC_PREFIX_START.len_utf8()..]))
527 } else {
528 None
529 },
530 command: {
532 String::from(line.split(char::is_whitespace).next().unwrap())
533 },
534 params: {
535 match line.splitn(2, char::is_whitespace).skip(1).next() {
536 Some(line) => ParamsIter::new(line).map(|s| String::from(s)).collect(),
537 None => Vec::new(),
538 }
539 }
540 })
541 }
542
543 pub fn command(&self) -> &str {
544 &self.command
545 }
546
547 pub fn has_prefix(&self) -> bool {
548 self.prefix.is_some()
549 }
550
551 pub fn origin(&self) -> Option<&str> {
552 match &self.prefix {
553 Some(p) => Some(p.origin()),
554 None => None,
555 }
556 }
557
558 pub fn user(&self) -> Option<&str> {
559 match &self.prefix {
560 Some(p) => p.user(),
561 None => None,
562 }
563 }
564
565 pub fn host(&self) -> Option<&str> {
566 match &self.prefix {
567 Some(p) => p.host(),
568 None => None,
569 }
570 }
571
572 pub fn params(&self) -> std::slice::Iter<String> {
573 self.params.iter()
574 }
575
576 pub fn param(&self, ind: usize) -> Option<&str> {
577 match self.params.iter().skip(ind).next() {
578 Some(s) => Some(s),
579 None => None,
580 }
581 }
582
583 pub fn tags(&self) -> std::slice::Iter<Tag> {
584 self.tags.iter()
585 }
586
587 pub fn tag(&self, key: &str) -> Option<&Tag> {
588 self.tags.iter().filter(|t| t.key() == key).next()
589 }
590
591 pub fn with_prefix(mut self, origin: &str, user: Option<&str>, host: Option<&str>) -> Message {
593 self.prefix = Some(Prefix::new(origin, user, host));
594 self
595 }
596
597 pub fn with_tag(self, key: &str, value: Option<&str>) -> Message {
598 self.with_tag_vendor(key, value, None)
599 }
600
601 pub fn with_tag_vendor(mut self, key: &str, value: Option<&str>, vendor: Option<&str>) -> Message {
602 self.tags.push(Tag::new_with_vendor(key, value, vendor));
603 self
604 }
605
606 pub fn with_param(mut self, param: &str) -> Message {
607 self.params.push(String::from(param));
608 self
609 }
610}
611
612impl Display for Message {
613 fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
614 if self.tags.len() > 0 {
615 f.write_str("@")?;
616 for (i, t) in self.tags.iter().enumerate() {
617 if i > 0 {
618 f.write_str(";")?;
619 }
620
621 write!(f, "{}", t)?;
622 }
623 f.write_str(" ")?;
624 }
625
626 if let Some(prefix) = &self.prefix {
627 write!(f, "{}", prefix)?;
628 f.write_str(" ")?;
629 }
630
631 f.write_str(&self.command)?;
632
633 if self.params.len() > 0 {
634 f.write_str(" ")?;
635
636 for (i, p) in self.params.iter().enumerate() {
637 if i > 0 {
638 f.write_str(" ")?;
639 }
640
641 if i >= self.params.len() - 1 {
643 if p.split(char::is_whitespace).count() > 1 {
645 f.write_str(":")?;
646 f.write_str(p)?;
647 } else {
648 f.write_str(p)?;
649 }
650 } else {
651 f.write_str(p)?;
652 }
653 }
654 }
655
656 Ok(())
657 }
658}