use std::fmt;
use std::fmt::{Display, Formatter};
use crate::builder::Message as MessageBuilder;
use crate::errors::InvalidIrcFormatError;
use crate::params::Params;
use crate::prefix::Prefix;
use crate::tags::Tags;
use std::convert::TryFrom;
#[derive(Debug, Clone, Eq, Ord, PartialOrd, PartialEq, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Message {
raw: String,
}
impl Message {
pub fn builder(command: &str) -> MessageBuilder {
MessageBuilder::new(command)
}
pub fn to_builder(&self) -> Result<MessageBuilder<'_>, InvalidIrcFormatError> {
let mut builder = MessageBuilder::new(self.command());
if let Some(tags) = self.tags()? {
for (key, value) in tags.iter() {
builder = builder.tag(key, value)
}
}
if let Some(prefix) = self.prefix()? {
let (name, user, host) = prefix.into_parts();
builder = builder.prefix(name, user, host);
}
builder = builder.command(self.command());
if let Some(params) = self.params() {
let (params, trailing) = params.into_parts();
if let Some(trailing) = trailing {
builder = builder.trailing(trailing);
}
for param in params {
builder = builder.param(param);
}
}
Ok(builder)
}
pub fn tags(&self) -> Result<Option<Tags>, InvalidIrcFormatError> {
if self.raw.starts_with('@') {
let end = self.raw.find(' ');
if let Some(end) = end {
Tags::try_from(&self.raw[1..end]).map(Some)
} else {
Err(InvalidIrcFormatError::NoTagEnd(self.raw.clone()))
}
} else {
Ok(None)
}
}
pub fn prefix(&self) -> Result<Option<Prefix>, InvalidIrcFormatError> {
let offset = self
.tags()
.map(|tags| {
tags.map(|tags| tags.len_raw() + 2)
})?
.unwrap_or(0);
Ok(match self.raw.chars().nth(offset) {
Some(':') => match self.raw[offset..].find(' ') {
Some(index) => Some(Prefix::from(&self.raw[offset + 1..offset + index])),
None => Some(Prefix::from(&self.raw[offset + 1..])),
},
_ => None,
})
}
pub fn command(&self) -> &str {
let without_tags = match self.raw.find(' ') {
Some(start) => {
if self.raw.starts_with('@') {
&self.raw[start + 1..]
} else {
&self.raw
}
}
None => &self.raw,
};
let without_prefix = match without_tags.find(' ') {
Some(start) => {
if without_tags.starts_with(':') {
&without_tags[start + 1..]
} else {
without_tags
}
}
None => &self.raw,
};
match without_prefix.find(' ') {
Some(end) => &without_prefix[..end],
None => without_prefix,
}
}
pub fn params(&self) -> Option<Params> {
let command = self.command();
let cmd_start = self.raw.find(command).unwrap();
self.raw[cmd_start..]
.find(' ')
.map(|param_start| Params::from(&self.raw[cmd_start + param_start..]))
}
}
impl Display for Message {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
self.raw.fmt(f)
}
}
impl From<String> for Message {
fn from(raw: String) -> Self {
Message { raw }
}
}
impl From<&str> for Message {
fn from(raw: &str) -> Self {
Message {
raw: raw.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use crate::message::Message;
#[test]
#[cfg(feature = "serde")]
fn test_serde() {
let message =
Message::from("@test=test :user@prefix!host COMMAND param :trailing".to_string());
let serialized = serde_json::to_string(&message).unwrap();
println!("Ser: {}", serialized);
let deserialized: Message = serde_json::from_str(serialized.as_str()).unwrap();
assert_eq!(deserialized.to_string(), message.to_string());
}
#[test]
fn test_tags() {
let message =
Message::from("@test=test :user@prefix!host COMMAND param :trailing".to_string());
let tags = message.tags();
assert!(tags.is_ok(), "{:?}", tags);
let tags = tags.unwrap();
assert!(tags.is_some(), "{:?}", tags);
}
#[test]
fn test_prefix() {
let message =
Message::from("@test=test :user@prefix!host COMMAND param :trailing".to_string());
let prefix = message.prefix();
assert!(prefix.is_ok(), "{:?}", prefix);
let prefix = prefix.unwrap();
assert!(prefix.is_some(), "{:?}", prefix);
}
}