use std::fmt::{Debug, Display, Error as FmtError, Formatter};
use std::result;
use std::error;
use super::escaping::{escape, unescape};
pub const IRC_TRAILING: char = ':';
pub const IRC_TAG_START: char = '@';
pub const IRC_PREFIX_START: char = ':';
pub const IRC_TAG_VALUE_SEP: char = '=';
pub const IRC_TAG_VENDOR_SEP: char = '/';
pub const IRC_TAG_END_SEP: char = ';';
pub const IRC_PREFIX_USER_SEP: char = '!';
pub const IRC_PREFIX_HOST_SEP: char = '@';
pub type Result<T> = result::Result<T, ParseError>;
fn check_valid_key(ch: char) -> bool { ch.is_alphabetic() || ch.is_digit(10) || ch == '-' }
fn get_char_at(s: &str, ind: usize) -> char {
s[ind..].chars().next().unwrap()
}
pub struct ParseError {
message: &'static str,
kind: ParseErrorKind,
}
impl ParseError {
fn new_unexpected(msg: &'static str, ch: char) -> ParseError {
ParseError {
message: msg,
kind: ParseErrorKind::Unexpected(ch)
}
}
fn new_bad_syntax(msg: &'static str) -> ParseError {
ParseError {
message: msg,
kind: ParseErrorKind::BadSyntax
}
}
fn new_missing_command(msg: &'static str) -> ParseError {
ParseError {
message: msg,
kind: ParseErrorKind::BadSyntax,
}
}
}
impl error::Error for ParseError {
}
impl Display for ParseError {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
f.write_str(self.message)?;
f.write_str("; ")?;
match &self.kind {
ParseErrorKind::Unexpected(ch) => write!(f, "unexpected char '{}'", ch),
ParseErrorKind::BadSyntax => f.write_str("bad syntax"),
ParseErrorKind::MissingCommand => f.write_str("missing command"),
}
}
}
impl Debug for ParseError {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
<ParseError as Display>::fmt(self, f)
}
}
pub enum ParseErrorKind {
Unexpected(char),
BadSyntax,
MissingCommand,
}
#[derive(PartialEq)]
pub struct Tag {
key: String,
vendor: Option<String>,
value: Option<String>,
}
impl Tag {
pub fn new(key: &str, value: Option<&str>) -> Tag {
Tag::new_with_vendor(key, value, None)
}
pub fn new_with_vendor(key: &str, value: Option<&str>, vendor: Option<&str>) -> Tag {
Tag {
key: String::from(key),
vendor: match vendor {
Some(s) => Some(String::from(s)),
None => None,
},
value: match value {
Some(s) => Some(String::from(s)),
None => None,
}
}
}
pub fn key(&self) -> &str {
&self.key
}
pub fn vendor(&self) -> Option<&str> {
match &self.vendor {
Some(s) => Some(s),
None => None,
}
}
pub fn value(&self) -> Option<&str> {
match &self.value {
Some(s) => Some(s),
None => None,
}
}
pub fn set_key(&mut self, key: &str) {
self.key = String::from(key);
}
pub fn set_vendor(&mut self, vendor: &str) {
self.vendor = Some(String::from(vendor));
}
pub fn set_value(&mut self, value: &str) {
self.value = Some(String::from(value));
}
}
impl Display for Tag {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
if let Some(ven) = &self.vendor {
f.write_str(ven)?;
f.write_str("/")?;
}
f.write_str(&self.key)?;
if let Some(value) = &self.value {
f.write_str("=")?;
f.write_str(&escape(value))?;
}
Ok(())
}
}
impl Debug for Tag {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
f.write_str(&self.key)?;
if let Some(val) = &self.value { write!(f, "=\"{}\"", val)?; }
if let Some(ven) = &self.vendor { write!(f, " ({})", ven)?; }
Ok(())
}
}
#[derive(PartialEq)]
pub struct Prefix {
origin: String,
user: Option<String>,
host: Option<String>,
}
impl Prefix {
pub fn new(origin: &str, user: Option<&str>, host: Option<&str>) -> Prefix {
Prefix {
origin: String::from(origin),
user: match user {
Some(s) => Some(String::from(s)),
None => None,
},
host: match host {
Some(s) => Some(String::from(s)),
None => None,
}
}
}
pub fn parse(pre: &str) -> Prefix {
Prefix {
origin: match pre.find(IRC_PREFIX_USER_SEP) {
Some(i) => String::from(&pre[..i]),
None => match pre.find(IRC_PREFIX_HOST_SEP) {
Some(i) => String::from(&pre[..i]),
None => String::from(pre),
}
},
user: match pre.find(IRC_PREFIX_USER_SEP) {
Some(i) => match pre.find(IRC_PREFIX_HOST_SEP) {
Some(j) => Some(String::from(&pre[(i+IRC_PREFIX_USER_SEP.len_utf8())..j])),
None => Some(String::from(&pre[(i+IRC_PREFIX_USER_SEP.len_utf8())..])),
},
None => None,
},
host: match pre.find(IRC_PREFIX_HOST_SEP) {
Some(i) => Some(String::from(&pre[(i+IRC_PREFIX_HOST_SEP.len_utf8())..])),
None => None,
}
}
}
pub fn origin(&self) -> &str {
&self.origin
}
pub fn user(&self) -> Option<&str> {
match &self.user {
Some(s) => Some(s),
None => None,
}
}
pub fn host(&self) -> Option<&str> {
match &self.host {
Some(s) => Some(s),
None => None,
}
}
pub fn set_origin(&mut self, s: &str) {
self.origin = String::from(s);
}
pub fn set_user(&mut self, s: &str) {
self.user = Some(String::from(s));
}
pub fn set_host(&mut self, s: &str) {
self.host = Some(String::from(s));
}
}
impl Display for Prefix {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
f.write_str(&self.origin)?;
if let Some(user) = &self.user {
f.write_str("!")?;
f.write_str(user)?;
}
if let Some(host) = &self.host {
f.write_str("@")?;
f.write_str(host)?;
}
Ok(())
}
}
impl Debug for Prefix {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
f.write_str("{")?;
write!(f, "origin=\"{}\"", self.origin)?;
if let Some(user) = &self.user {
write!(f, ", user=\"{}\"", user)?;
}
if let Some(host) = &self.host {
write!(f, ", host=\"{}\"", host)?;
}
f.write_str("}")
}
}
pub struct TagsIter<'a> {
cursor: usize,
inner: &'a str,
}
impl<'a> TagsIter<'a> {
pub fn new(tags: &'a str) -> TagsIter<'a> {
TagsIter {
cursor: 0,
inner: tags,
}
}
}
impl<'a> Iterator for TagsIter<'a> {
type Item = Result<Tag>;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.inner.len() {
return None
}
let mut vendor: Option<String> = None;
let mut has_value = false;
let mut start = self.cursor;
while self.cursor < self.inner.len() {
let ch = get_char_at(self.inner, self.cursor);
if ch == IRC_TAG_VENDOR_SEP {
if vendor.is_none() {
vendor = Some(String::from(&self.inner[start..self.cursor]));
start = self.cursor + ch.len_utf8();
} else {
return Some(Err(ParseError::new_bad_syntax(
"tags may only have one vendor")));
}
self.cursor += ch.len_utf8();
continue;
}
if ch == IRC_TAG_VALUE_SEP {
has_value = true;
break;
}
if ch == IRC_TAG_END_SEP {
break;
}
if !check_valid_key(ch) {
return Some(Err(ParseError::new_unexpected(
"tags can only contain letters, digits or hyphens", ch)));
}
self.cursor += ch.len_utf8();
}
let tag = Tag{
key: String::from(&self.inner[start..self.cursor]),
vendor: vendor,
value: if has_value {
self.cursor += IRC_TAG_VALUE_SEP.len_utf8();
let start = self.cursor;
while self.cursor < self.inner.len() {
let ch = get_char_at(self.inner, self.cursor);
if ch == IRC_TAG_END_SEP {
break;
}
self.cursor += ch.len_utf8();
}
Some(unescape(&self.inner[start..self.cursor]))
} else {
None
},
};
self.cursor += IRC_TAG_END_SEP.len_utf8();
Some(Ok(tag))
}
}
pub struct ParamsIter<'a> {
cursor: usize,
inner: &'a str,
}
impl<'a> ParamsIter<'a> {
pub fn new(params: &'a str) -> ParamsIter<'a> {
ParamsIter {
cursor: 0,
inner: params,
}
}
}
impl<'a> Iterator for ParamsIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
if self.cursor >= self.inner.len() {
return None;
}
let mut trailing = false;
let mut start = self.cursor;
let mut offset: usize = 0;
while self.cursor < self.inner.len() {
let ch = get_char_at(self.inner, self.cursor);
if ch.is_whitespace() && !trailing {
offset += ch.len_utf8();
while self.cursor+offset < self.inner.len() {
let ch = get_char_at(self.inner, self.cursor+offset);
if !ch.is_whitespace() {
break;
}
offset += ch.len_utf8();
}
break;
}
if start == self.cursor {
if ch == IRC_TRAILING {
trailing = true;
start += ch.len_utf8();
}
}
self.cursor += ch.len_utf8();
}
let slice = &self.inner[start..self.cursor];
self.cursor += offset;
Some(slice)
}
}
#[derive(Debug, PartialEq)]
pub struct Message {
tags: Vec<Tag>,
prefix: Option<Prefix>,
command: String,
params: Vec<String>,
}
impl Message {
pub fn new(command: &str) -> Message {
Message {
tags: Vec::new(),
prefix: None,
command: String::from(command),
params: Vec::new(),
}
}
pub fn parse<S: AsRef<str> + ?Sized>(s: &S) -> Result<Message> {
let mut line = s.as_ref();
if line.is_empty() {
return Err(ParseError::new_missing_command("missing irc command!"))
}
Ok(Message {
tags: if line.starts_with(IRC_TAG_START) {
let mut split = line.splitn(2, char::is_whitespace);
let tags = split.next().unwrap();
line = match split.next() {
Some(line) => line,
None => return Err(ParseError::new_missing_command("missing irc command!")),
};
TagsIter::new(&tags[IRC_TAG_START.len_utf8()..]).filter_map(|r| match r {
Ok(t) => Some(t),
Err(_) => None,
}).collect()
} else {
Vec::new()
},
prefix: if line.starts_with(IRC_PREFIX_START) {
let mut split = line.splitn(2, char::is_whitespace);
let prefix = split.next().unwrap();
line = match split.next() {
Some(line) => line,
None => return Err(ParseError::new_missing_command("missing irc command!")),
};
Some(Prefix::parse(&prefix[IRC_PREFIX_START.len_utf8()..]))
} else {
None
},
command: {
String::from(line.split(char::is_whitespace).next().unwrap())
},
params: {
match line.splitn(2, char::is_whitespace).skip(1).next() {
Some(line) => ParamsIter::new(line).map(|s| String::from(s)).collect(),
None => Vec::new(),
}
}
})
}
pub fn command(&self) -> &str {
&self.command
}
pub fn has_prefix(&self) -> bool {
self.prefix.is_some()
}
pub fn origin(&self) -> Option<&str> {
match &self.prefix {
Some(p) => Some(p.origin()),
None => None,
}
}
pub fn user(&self) -> Option<&str> {
match &self.prefix {
Some(p) => p.user(),
None => None,
}
}
pub fn host(&self) -> Option<&str> {
match &self.prefix {
Some(p) => p.host(),
None => None,
}
}
pub fn params(&self) -> std::slice::Iter<String> {
self.params.iter()
}
pub fn param(&self, ind: usize) -> Option<&str> {
match self.params.iter().skip(ind).next() {
Some(s) => Some(s),
None => None,
}
}
pub fn tags(&self) -> std::slice::Iter<Tag> {
self.tags.iter()
}
pub fn tag(&self, key: &str) -> Option<&Tag> {
self.tags.iter().filter(|t| t.key() == key).next()
}
pub fn with_prefix(mut self, origin: &str, user: Option<&str>, host: Option<&str>) -> Message {
self.prefix = Some(Prefix::new(origin, user, host));
self
}
pub fn with_tag(self, key: &str, value: Option<&str>) -> Message {
self.with_tag_vendor(key, value, None)
}
pub fn with_tag_vendor(mut self, key: &str, value: Option<&str>, vendor: Option<&str>) -> Message {
self.tags.push(Tag::new_with_vendor(key, value, vendor));
self
}
pub fn with_param(mut self, param: &str) -> Message {
self.params.push(String::from(param));
self
}
}
impl Display for Message {
fn fmt(&self, f: &mut Formatter) -> result::Result<(), FmtError> {
if self.tags.len() > 0 {
f.write_str("@")?;
for (i, t) in self.tags.iter().enumerate() {
if i > 0 {
f.write_str(";")?;
}
write!(f, "{}", t)?;
}
f.write_str(" ")?;
}
if let Some(prefix) = &self.prefix {
write!(f, "{}", prefix)?;
f.write_str(" ")?;
}
f.write_str(&self.command)?;
if self.params.len() > 0 {
f.write_str(" ")?;
for (i, p) in self.params.iter().enumerate() {
if i > 0 {
f.write_str(" ")?;
}
if i >= self.params.len() - 1 {
if p.split(char::is_whitespace).count() > 1 {
f.write_str(":")?;
f.write_str(p)?;
} else {
f.write_str(p)?;
}
} else {
f.write_str(p)?;
}
}
}
Ok(())
}
}